Django Minimalist Custom User Model
In this article I will look at creating a minimal custom user model in Django.
The aim is to create the simplest possible user model, providing only a username field and a password field, that will allow logging in, logging out and testing authorisation with the standard Django auth interface.
Being as small as possible is the goal at all costs. The user model we create here will not be suitable for accessing the Django admin site, as the admin requires fields that we will be leaving out.
This user model is unlikely to be usable as it stands, but provides a clean slate on which a customised user model could be built, without clutter in the background.
Research - AbstractBaseUser
Online sources including the official Django documentation suggest we may want to build our custom user model on top of the AbstractBaseUser class. We will begin there, and it is instructive to first examine the code in the libary for AbstractBaseUser.
django/lib/python3.8/site-packages/django/contrib/auth/base_user.py
[...] class AbstractBaseUser(models.Model): password = models.CharField(_('password'), max_length=128) last_login = models.DateTimeField(_('last login'), blank=True, null=True) is_active = True REQUIRED_FIELDS = [] [...]
Examining AbstractBaseUser in the django library, a password field is already provided for us, along with a last login field, a fixed is_active property, and the REQUIRED_FIELDS empty list)
Coding Our Custom User Model
In this example, we have named our custom user app 'customusers'.
We will be writing our classes in 'customusers/models.py', building on BaseUserManager and AbstractBaseUser, so we include these at the top of the file.
customusers/models.py
from django.db import models from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
The User Manager
Our custom user model will require a custom user manager, whose only required purpose is to handle creation of users. The user manager is set as the 'objects' property on the user model, and inherits from BaseUserManager, which in turn inherits from models.Manager, therefore inheriting the usual database management methods.
Django passwords are stored as a hash and therefore need to be set using the set_password function (provided by the AbstractBaseUser class).
The create_superuser method is required so `manage.py createsuperuser` will work.
customusers/models.py
class CustomUserManager(BaseUserManager): def create_user(self, username, password): user = CustomUser.objects.create(username=username) user.set_password(password) user.save() return user def create_superuser(self, username, password): user = self.create_user(username, password) return user
The User Model
The field used for user identification must have unique=True set, and must be named in the USERNAME_FIELD property. It can otherwise be any kind of field we like, we could for instance use an email field as the username.
As we discovered above, a last_login field is provided by the
AbstractBaseUser class; if we desire to go really minimalist, it can be disabled
by setting last_login = None
in our custom user model.
The objects property is required and must be set to our custom user manager class.
customusers/models.py
class CustomUser(AbstractBaseUser): username = models.CharField(max_length=50, unique=True) last_login = None USERNAME_FIELD = 'username' objects = CustomUserManager()
Using Our CustomUser Model
The above minimalist code is sufficient for user login, logout and authorisation testing and so achieves what we set out to do.
In the interest of completeness, I will summarise how to deploy the user model we have created on our site.
Settings.py
To use our custom user model we must add it to INSTALLED_APPS in
settings.py in the usual manner, and additionally also add
AUTH_USER_MODEL = 'customusers.CustomUser'
somewhere
(I favour just after the AUTH_PASSWORD_VALIDATORS list, above the
# Internationalization
comment in the
Django-generated settings.py).
Login and Logout Views
Here is an example of the views required to perform login and logout functions.
customusers/views.py
from django.shortcuts import redirect from django.contrib.auth import authenticate, login as auth_login, logout as auth_logout def login(request): user = authenticate(request, username=request.POST['username'], password=request.POST['password']) if user is None: # invalid login return redirect('error') # successful login auth_login(request, user) return redirect('home') def logout(request): auth_logout(request) return redirect('home')
A login form should action a POST request on the url for the login view, and the logout view can be called as a GET request from an ordinary hyperlink.
Testing Authorisation
The visitor's login/logout status may be tested in a view using
request.user.is_authenticated
:
demo/views.py
def home(request): if request.user.is_authenticated: message = "You are logged in." else: message = "You are not logged in."
The same can also be tested in the template. Assuming the view's request.user has been passed in the template context as user:
demo/templates/home.html
{% if user.is_authenticated %} <p>You are logged in as "{{ user.username }}" - <a href=/logout>Logout</a> {% else %} <form method=post action=/login> <input type=text name=username> <input type=password name=password> <input type=submit value=Login> {% csrf_token %} </form> {% endif %}
Summary
In this article I have demonstrated the most minimalist Django custom user model possible.
The code for this model and a project demonstrating it is available on GitHub.