DEV Community

Hannah
Hannah

Posted on • Updated on

Custom User Model in Django

Hello everyone. Follow this article to understand about custom user model in Django.


I've attached the source code at the end of this article.

Django's built-in User model is good enough for many cases while also having a room for customization. Why would you need to tweak some of the functionalities of the default User model?

  • Maybe you want to make email field the primary unique identifier of users instead of username.
  • Maybe you want to include other unique fields for authentication like phone number.
  • Maybe you want to stack all user information, be it auth related or non-auth fields all in the User model.

That being said, let's get to it.


Step 1: Set up a Django project.

1) Create a python virtual environment and activate it.

2) Install Django and Django rest framework.

pip install django djangorestframework
Enter fullscreen mode Exit fullscreen mode

3) Start a new project:

django-admin startproject config .
Enter fullscreen mode Exit fullscreen mode

4) Run the following command to see if the installation is correct.

py manage.py runserver
Enter fullscreen mode Exit fullscreen mode

Note: Do not create and apply migrations because changing the User model after you’ve created database tables is significantly more difficult since it affects foreign keys and many-to-many relationships.

Now, create a new app users and add it to installed apps.

py manage.py startapp users
Enter fullscreen mode Exit fullscreen mode

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # Third-party apps
    'rest_framework',

    # Local apps
    'users',
]
Enter fullscreen mode Exit fullscreen mode

When you have projects that require a different authentication that the built-in User model can’t provide, you have to create your own custom User model by extending from either AbstractUser or AbstractBaseUser Now, the question is which one should you extend from?

  1. AbstractUser : Are you satisfied with the existing fields in the built-in User model but you want to use email as the primary unique identifier of your users or perhaps remove the username field? If yes, AbstractUser is the right option for you.
  2. AbstractBaseUser : There are two things to consider whenever starting a new project :
    • User model focused on authentication, and another model like Profile that keeps app-related information of the user. Here you may use AbstractBaseUser if you have additional auth-related attributes that you want to include in your User model.
    • User model that stores all information (auth related or non-auth attributes) all in one. You may want to do this to avoid using additional database queries to retrieve related model. If you want to take this approach, AbstractBaseUser is the right option.

Note: If you’re starting a new project, it’s highly recommended to set up a custom user model, even if the default User model is sufficient for you. This model behaves identically to the default user model, but you’ll be able to customize it in the future if the need arises:

from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    pass
Enter fullscreen mode Exit fullscreen mode

For the purpose of this article, we are going to use AbstractUser but the steps for AbstractBaseUser are also similar.

Step 2: Custom Model Manager

Manager is a class that provides an interface through which database query operations are provided to Django models. You can have more than one manager for your model.

Consider this model:

from django.db import models

class Car(models.Model):
    pass
Enter fullscreen mode Exit fullscreen mode
  • To get all instances of Car, you will use Car.objects.all()

objects is the default name that Django managers use. To change this name:

from django.db import models

class Car(models.Model):
    cars = models.Manager();
Enter fullscreen mode Exit fullscreen mode

Now, to get all instances of car, you should use Car.cars.all().

For our custom user model, we need to define a custom manager class because we are going to modify the initial Queryset that the default Manager class returns. We do this by extending from BaseUserManager and providing two additional methods create_user and create_superuser.

  • Create a file named managers.py inside users app and put the following.

managers.py

from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import gettext as _

class CustomUserManager(BaseUserManager):
    """
    Custom user model manager where email is the unique identifier
    for authentication instead of usernames.
    """

    def create_user(self, email, password, **extra_fields):
        if not email:
            raise ValueError(_('Users must have an email address'))
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save()
        return user

    def create_superuser(self, email, password, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError(_('Superuser must have is_staff=True.'))
        if extra_fields.get('is_superuser') is not True:
            raise ValueError(_('Superuser must have is_superuser=True.'))
        return self.create_user(email, password, **extra_fields)

Enter fullscreen mode Exit fullscreen mode

Step 3: The User Model

models.py

from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import gettext as _

from .managers import CustomUserManager

class CustomUser(AbstractUser):
    email = models.EmailField(_('email address'), unique=True)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ('username',)

    objects = CustomUserManager()

    def __str__(self):
        return self.email

Enter fullscreen mode Exit fullscreen mode
  • USERNAME_FIELD specifies the name of the field on the user model that is used as the unique identifier. In our case it’s email.
  • REQUIRED_FIELDS A list of the field names that will be prompted for when creating a superuser via the createsuperuser management command. This doesn’t have any effect in other parts of Django like when creating a user in the admin panel.

Step 4: The Settings

We now have to tell Django our new model that should be used to represent a User. This is done as follows.

settings.py

AUTH_USER_MODEL = 'users.CustomUser'
Enter fullscreen mode Exit fullscreen mode

→ You can now create and apply migrations.

py manage.py makemigrations
py manage.py migrate
Enter fullscreen mode Exit fullscreen mode

Step 5: Forms

Django’s built-in UserCreationForm and UserChangeForm forms must be extended to let them know the new user model that we are working with.

Create a file named forms.py inside users app and add the following:

forms.py

from django.contrib.auth.forms import UserCreationForm, UserChangeForm

from .models import CustomUser

class CustomUserCreationForm(UserCreationForm):

    class Meta:
        model = CustomUser
        fields = ('email',)

class CustomUserChangeForm(UserChangeForm):

    class Meta:
        model = CustomUser
        fields = ('email',)
Enter fullscreen mode Exit fullscreen mode

Step 6: Admin

Tell the admin panel to use these forms by extending from UserAdmin in users/admin.py

admin.py

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

from .forms import CustomUserCreationForm, CustomUserChangeForm
from .models import CustomUser

class CustomUserAdmin(UserAdmin):
    add_form = CustomUserCreationForm
    form = CustomUserChangeForm

    model = CustomUser

    list_display = ('username', 'email', 'is_active',
                    'is_staff', 'is_superuser', 'last_login',)
    list_filter = ('is_active', 'is_staff', 'is_superuser')
    fieldsets = (
        (None, {'fields': ('username', 'email', 'password')}),
        ('Permissions', {'fields': ('is_staff', 'is_active',
         'is_superuser', 'groups', 'user_permissions')}),
        ('Dates', {'fields': ('last_login', 'date_joined')})
    )
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('username', 'email', 'password1', 'password2', 'is_staff', 'is_active')}
         ),
    )
    search_fields = ('email',)
    ordering = ('email',)

admin.site.register(CustomUser, CustomUserAdmin)
Enter fullscreen mode Exit fullscreen mode
  • add_form and form specifies the forms to add and change user instances.
  • fieldsets specifies the fields to be used in editing users and add_fieldsets specifies fields to be used when creating a user.

You can now go to the admin panel and add/edit users.

Source code: https://github.com/earthcomfy/jwt-auth

Discussion (0)