Customizing User details, User models in Django

(Comments)

Django's authentication system is highly customizable. Yet the default authentication is good enough for most use cases. However we might need to customize to suite our needs. We will explore the various methods of customization.

Extending User model details

The default Django's user model is django.contrib.auth.model.User and it stores username, first_name, last_name, email and some other meta data. The best way to add more details is to define a new model with a OneToOneField related to the User model.


For example let us define model called Profile which can store some extra details of the user.

from django.contrib.auth.models import User

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    department = models.CharField(max_length=100)
    date_of_birth = models.DateField()

We can now add additional data of the user using the Profile model. We can add the data to the model at the time of registration, something like this.

def register(request):
    # Validate the user registration data
    if userForm.is_valid():
        email = userForm.cleaned_data['email']
        username = userForm.cleaned_data['username']
        password = userForm.cleaned_data['password']
        user = User.objects.create_user(email=email, username=username, password=password)
        # Then save the profile details
        dob = userForm.cleaned_data['date_of_birth']
        department = userForm.cleaned_data['department']
        profile = Profile.objects.create(user=user, date_of_birth=dob, department=department)

While this is ok, it is possible that only the User model is saved and creation of Profile object fails for some reason. This can fail the integrity of the data. Such problems can be avoided with the use of atomic transactions. Django's transactions allow us to perform all the database activities as an atomic transaction i.e., either all the database operations are complete or none of them is saved. This will make our system consistent.


Django's transactions are easy to use. We can use the transaction.atomic() context manager with with and perform all the database operations withing the same block. The implementation will make things more clear.

from django.db import transaction, DatabaseError

def register(request):
    # Validate the user registration data
    if userForm.is_valid():
        email = userForm.cleaned_data['email']
        username = userForm.cleaned_data['username']
        password = userForm.cleaned_data['password']
        dob = userForm.cleaned_data['date_of_birth']
        department = userForm.cleaned_data['department']

        try:
            with transaction.atomic():
                # All the database operations within this block are part of the transaction
                user = User.objects.create_user(email=email, username=username, password=password)
                profile = Profile.objects.create(user=user, date_of_birth=dob, department=department)
         except DatabaseError:
             # The transaction has failed. Handle appropriately
             pass

With this piece of code we can be sure that either both the models User and Profile are saved to the database or none is saved in which case we catch the error and handle it appropriately. Transactions are life savers when we want to ensure integrity in our applications.

Using custom User model

Django allows us to use a custom model to represent a user. To do this, we can extend the django.contrib.auth.models.AbstractUser abstract model.

from django.contrib.auth.models import AbstractUser

class CustomUser(AbstractUser):
    department = models.CharField(max_length=100)
    date_of_birth = models.DateField()

The AbstractUser model already defines the required fields like username, email, password etc., attributes and methods. We only need to define the additional fields that we need. But we don't always need to extend the AbstractUser. Instead we can use any model that we want. Once we are ready with our model set the value of AUTH_USER_MODEL in the project settings to the class name (string) of the user model.

AUTH_USER_MODEL = 'myapp.models.CustomUser'

With this setting Django recognizes the model used to represent the users of the application. We can use the regular of creating user model (don't forget to use the set_password() method to set a user's password) or we can create a model manager.

The AUTH_USER_MODEL setting

One more thing to keep in mind is how to we use the user model in our apps. If we are building an app that depends on the user model i.e., has a model that references user model, it cannot be used if the project uses a custom user model. To address this issue, we have to use the AUTH_USER_MODEL setting to specify the model.

# Use the following way to reference a user model
user = models.ForeignKey(
        settings.AUTH_USER_MODEL
    )

By this, our Django applications references the user model what ever the project uses making our application generic. While this is enough for most of the use cases, Django also provides more ways to customize the model and keep our applications generic. For example, even though we do not have the preferred fields like username , email etc, we can specify the field name that can be used to uniquely identify the user with the USERNAME_FIELD. Visit the section specifying a custom user model for more information.

Comments

Recent Posts

Archive

2021
2020
2019
2018
2017
2016
2015
2014

Tags

Authors

Feeds

RSS / Atom