The user authentication system provided by Django is extremely powerful and handles most of the authentication (Am I who I say I am?) and authorization (Am I authorized to do what I want to do?) needs of an web project. User accounts, groups and permissions, methods for handling passwords securely are part of this system.
Generally, this is adequate for most projects; however, there are situations where it becomes necessary to modify the behavior of a User or alter how their data is stored in the database. It should be noted that modifying the authorization and/or authentication process of a User will not be covered in this post.
Creating a user profile model is one solution to extend and modify the behavior of a User, as discussed before, but that approach has some caveats that are solved with the use of a custom user model.
Custom User model
We can tell Django which is the model that will replace the built-in
django.contrib.auth.models.User
model in the system. This is done by
providing a value to AUTH_USER_MODEL
in your project.
# myproject/myproject/settings.py
AUTH_USER_MODEL = "users.CustomUser"
Usually I create a
users
application to group all custom user related code to keep everything in the same context
It is highly recommended that you do this configuration at the beginning of the project, as after you have created your database tables, it will require you to fix your schema, moving data between tables and reapplying some migrations manually (see #25313 for an overview of the steps involved).
Now we need to create our custom model.
This new user model can handle different authentication and authorization schemes, can use different fields (e.g. email) to identify the user or any other requirement that is not satisfied using the default Django user model.
Adding new fields to default User model
If the default User model has everything we need and we want
to add some extra profile fields keeping authentication and authorization as
it is, create a custom model inheriting from AbstractUser
would be enough.
# myproject/users/models.py
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
date_of_birth = models.DateField(null=True)
# myproject/myproject/settings.py
AUTH_USER_MODEL = "users.CustomUser"
As we will not create a new table to store the user related information, there is no need for additional database queries to retrieve related models. We also don’t need to worry if a related model is created or not reducing the complexity of the system.
AbstractBaseUser
If we want to use a different field as the identifier (other than email
),
use a different authorization system and/or having more control on what
fields we have in our user model, a custom model inheriting from
AbstractBaseUser
is the choice.
AbstractBaseUser
is the core implementation of a User. It has a
password
field and methods to store and validate it securely. A
last_login
datetime field and everything else is up to us to provide.
If using the default authentication backend, our model must have a single unique field that will be the identifier of our user such as email address, username or any other unique attribute.
You configure that attribute using USERNAME_FIELD
that defines what is
the field that will uniquely identify the user.
EMAIL_FIELD
REQUIRED_FIELDS
is_active
is an attribute on AbstractBaseUser
defaulting to True
As an example, we will define a custom user that will use the email address as the identifier, has the date of birth as one required field can store the phone number and has a flag field in the database to indicate if the user is active or not.
# myproject/users/models.py
from django.contrib.auth.models import AbstractBaseUser
class CustomUser(AbstractBaseUser):
USERNAME_FIELD = "email"
EMAIL_FIELD = "email"
REQUIRED_FIELDS = ["date_of_birth", ]
date_of_birth = models.EmailField(unique=True)
email = models.EmailField(unique=True)
is_active = models.BooleanField(default=True)
phone_number = models.CharField(max_length=16, blank=True)
FROM DOCS Authentication backends provide an extensible system for when a username and password stored with the user model need to be authenticated against a different service than Django’s default.
TODO: list all fields that are created with this
Any other fields needs to be created.
is_active is a property set to True, but you can define if you want
USERNAME_FIELD is needed identifier unique = True EMAIL_FIELD to specifi what is the email field REQUIRED_FIELDS fields required when createsuperuser
If we want to use email
as the identifier of an user,
we need to set USERNAME_FIELD
property
# myproject/users/models.py
class CustomUser(AbstractBaseUser):
USERNAME_FIELD = "email"
email = models.EmailField()
One drawback is you need to specify a Custom Manager implementing
two methods: create_user
and create_superuser
. This is required
so we willll be able to run python manage.py createsuperuser
and python manage.py create-user
.
If some fields (TODO list what fields) from ABstractUser
are there, you can
use the built-in UserManager
FROM DOCS username, email, is_staff, is_active, is_superuser, last_login, and date_joined fields the same as Django’s default user, you can install Django’s UserManager
# myproject/users/models.py
class CustomUser(AbstractBaseUser):
USERNAME_FILED = "email"
email = models.EmailField()
objects = UserManager()
If you don’t want to use Django Admin (very unlikely), you’re done, and you can create/manage your users and authentication backend will be able.
However, if you want to use this custom user model with Django admin, there are some extra steps.
We need some fields and methods:
is_staff
so you have access to django admin
is_active
inactive users can’t access django admin
to manage permissions to edit registered models
has_perm
has_module_perms()
we don’t need to use the built-in permission system (see PermissionMixin)
And UserCreationForm
< UserChangeForm
and PasswordResetForm
(if we don’t have email field)
needs to be customized so we can use that.
Also we need a custom UserAdmin to use our custom forms.
This is a lot of things, and sometimes difficult to remember. The docs are ok to describe the process, but IMO this should be much simpler :-P
If by any chance you want to use django model permission, you can use PermissionMixin, so you have access to groups, etc (to be honest, I never saw anything using permission, but groups)
Sometimes the username may not be the way you want to identify an user (an email or social number would be more suitable for your project) or we have different requirements for authorization and permission. In those cases creating a custom user model is a option.
This has advatanges over the user of a user profile model and solve some of the caveats of that approach such as:
- We won’t need an extra database table to store your custom fields, so there is no performance problems to access them;
- As the table that we store the User data required for Django authentication is the same, we don’t need to bother if your profile fields will be available or not
When creating a custom user model, we have two different options If you are going to this path, you can create your custom user inheriting from two different classes:
If using Django Admin, we also need to register this new model.
# myproject/users/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser
admin.site.register(CustomUser, UserAdmin)