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 ofusername
. - 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
3) Start a new project:
django-admin startproject config .
4) Run the following command to see if the installation is correct.
py manage.py runserver
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
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',
]
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?
-
AbstractUser
: Are you satisfied with the existing fields in the built-inUser
model but you want to use email as the primary unique identifier of your users or perhaps remove theusername
field? If yes,AbstractUser
is the right option for you. -
AbstractBaseUser
: There are two things to consider whenever starting a new project :-
User
model focused on authentication, and another model likeProfile
that keeps app-related information of the user. Here you may useAbstractBaseUser
if you have additional auth-related attributes that you want to include in yourUser
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
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
- To get all instances of
Car
, you will useCar.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();
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)
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
-
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 thecreatesuperuser
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'
→ You can now create and apply migrations.
py manage.py makemigrations
py manage.py migrate
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',)
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)
-
add_form
andform
specifies the forms to add and change user instances. -
fieldsets
specifies the fields to be used in editing users andadd_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
Top comments (5)
Impressive thanks alot for this Hana
thank God i came across your explanation, this has been confusing for a while now, It is the simplicity of your explanation i love most , thanks Hana
Thanks a lot. I have been looking for hours all over the internet. This helped!
I'm happy to hear that
Very simple to follow. Thank you Hana.