Table of contents

How to customize the User model in Django?

How to customize the User model in Django?

In this post I explain three methods to extend or customize Django’s User model, without having to rewrite it from scratch, and keeping all Django’s user management features

But, before we start, let’s see where Django’s User model comes from.

Where does the Django User model come from?

Django’s User model inherits from AbstractUser which, in turn, inherits from the AbstractBaseUser class.

graph TD; AbstractBaseUser-->AbstractUser; AbstractUser-->User;

If you look at the Django source code, you will see that the User model you normally use has virtually no functionality of its own, but inherits all of its functionality from AbstractUser.

Screenshot of Django User model code
Screenshot of Django version 4.0 code

Now that we know the above, we can use the AbstractUser and AbstractBaseUser classes to create our custom User models.

Inherit from subclass AbstractUser

This method is probably the most popular method for extending Django’s User model. This is because it retains almost all the functionality of the original User model.

# users/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    # Tus propiedades personalizadas
    credits = models.PositiveIntegerField(verbose_name='credits',
        default=0, 
        blank=True)

After creating a new class that inherits from AbstractUser, we need to tell Django that we want to use this new model instead of the default user model.

We set this behavior in our configuration file.

# settings.py
AUTH_USER_MODEL = 'users.CustomUser'

Using the custom model in Django’s account views

If we want to use the Django template system to automatically generate a registration form, we will need to tell Django to use the new user model, for this we inherit a new form from the UserCreationForm class, and pass it our custom model, which we can with the get_user_model method.

from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm

User = get_user_model()

class RegisterFormForCustomUser(UserCreationForm):
    class Meta:
        model = User
        fields = ['username', 'email']

And that’s it, we can use it exactly as if we were using the User model included in Django.

django admin does not hashed passwords

When we use a custom user model, we need to tell Django to handle passwords with the default user functionality

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth import get_user_model

user = get_user_model()

class CustomUserAdmin(UserAdmin):
    pass

admin.site.register(user, CustomUserAdmin)

Now the admin panel will behave exactly as it would with the default Django user.

What does AbstractUser look like internally?

Notice how the AbstractUser class inherits from _AbstractBaseUse_r and has multiple fields available to profile a user. Also, it cannot be instantiated directly, as it is an abstract class.

Django AbstractUser class source code
Screenshot of Django version 4.0 AbstractUser code

Let’s move on to the second method.

How to change the user field for authentication?

If you look at the code above, there is an uppercase property called USERNAME_FIELD, there you can specify another field to work as the user. As you don’t want two users to identify themselves in the same way that field has to be marked as unique. Besides that you have to modify the object manager, the code is a bit long so I won’t put it here

class CustomUser(AbstractUser):
    custom_id = models.CharField(max_length=40, unique=True)
    # ...
    USERNAME_FIELD = 'custom_id'

Inherit from subclass AbstractBaseUser

This class, as you can see in the previous image, is the base class used to create the AbstractUser. Its operation is the minimum and it only has 3 fields:

  • password
  • last_login
  • is_active

It only has the authentication function. And you have to indicate which field will be used as username, to authenticate the user.

This method is usually used to fully customize the User model or when we need almost no extra fields.

# users/models.py
from django.contrib.auth.base_user import AbstractBaseUser
from django.db import models

class CustomUser(AbstractBaseUser):
    email = models.EmailField(verbose_name='emails', unique=True, max_length=255)
    credits = models.PositiveIntegerField(verbose_name='credits',
        default=0, 
        blank=True)
    USERNAME_FIELD='email'
    REQUIRED_FIELDS = []

Remember to tell Django to use your custom model instead of the default one.

# settings.py
AUTH_USER_MODEL = 'users.CustomUser'

What does AbstractBaseUser look like internally?

The following image is a direct screenshot of the Django code in version 4.0

As you can see, it only has the 3 fields mentioned, it inherits directly from models.Model and its Meta class tells Python that it is an abstract model; you cannot create instances directly from it.

Django AbstractBaseUser class source code
Screenshot of Django AbstractBaseUser version 4.0

Now let’s look at the third way to extend Django’s User model.

Create a profile to extend the model User

Another way to extend the user model is to create another model that serves as a container for the extra fields and then relate it by an OneToOneField field to the model that the Django configuration receives by default.

This approach is ideal if we are the creator of a package that needs to customize the User model of the project to work, but without modifying it directly.

It is also useful when we need several types of users or different profiles, with different fields between them.

To create a profile in this way it is enough to declare a field that relates our new model to the User model, by means of an OneToOneField.

from django.conf import settings
class Profile(models.Model):
    # other fields
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

And to access our user, we access the field that relates it to the model we created.

user = User.objects.get(username='user')
user.Profile

Other resources

Eduardo Zepeda
Web developer and GNU/Linux enthusiast always learning something new. I believe in choosing the right tool for the job and that simplicity is the ultimate sophistication. I'm under the impression that being perfect is the enemy of getting things done. I also believe in the goodnesses of cryptocurrencies outside of monetary speculation.
Read more