AbstractUser vs AbstractBaseUser : Creating a custom Django user model.

Photo by Faisal on Unsplash

AbstractUser vs AbstractBaseUser : Creating a custom Django user model.

Many times, as a Django project gets more complex, there will be more users and these users will later have diverse roles they play in this project. Permissions, roles, and unique identifiers as different from the normal email and username fields we have in our default “User” model. This is where Customization comes in.

Django is in itself defined as a framework to promote the “DRY” programming principle, where you don't have to repeat yourself when you need to implement functionalities slightly different from its default functionalities. Just “extend” or “inherit.”

There are two Django default user models we can extend to implement these: the “AbstractBaseUser” and “AbstractUser” models.

In this text, I'm going to walk us through an instance that has been implemented before. And works.

Note: The code instances we are going to use here are not fixed. As a framework, there are different methods to get tasks done, however, similarities may exist.

“AbstractBaseUser” is less complicated compared to “AbstractUser” and is preliminary. It takes just a few fields. Just the basic “username”, “is_active”, “is_staff”, and “password” fields.

It will be a handful to note that when extending “AbstractBaseUser”, the fields “USERNAME_FIELD”, “EMAIL_FIELD” and “REQUIRED_FIELDS” need to be set.

from django.contrib.auth.models import AbstractBaseUser, BaseUserManger
class myCustomUserModel(AbstractBaseUser):
    username = models.Charfield(max_length=30)
    password = models.CharField(max_length=31)
    last_login = models.DateTimeField()
    date_joined= models.DateTimeField(auto_now_add=True, default = datetime.now)

    EMAIL_FIELD = ''
    USERNAME_FIELD = ''
    REQUIRED_FIELDS = []

    def __str__(self):
        return self.get_full_name

“AbstractUser” on the other hand is:

"""

An abstract base class implementing a fully featured User model with

admin-compliant permissions.

Username and password are required. Other fields are optional. """

What “AbstractUser” solves differently from “AbstractBaseUser” is more fields, more methods and more parameters to create a user object. If your intention is to add fields like “student_no” and pass them as a parameter to create any user object on your site, then AbstractUser is the way. It allows you to set your password by calling a super() method when you define a new method, or simply customize your CustomUserManager by extending “BaseUserManger” to access the “AbstractBaseUser” set_password() method. See:

from django.contrib.auth.models import AbstractUser, BaseUserManager
class yourUserModel(AbstractUser):
    password= models.CharField(max_length=128)
    email = models.EmailField(max_length=128, unique=True)
    first_name = models.CharField(max_length=128)
    last_name = models.CharField(max_length=128)
    username = models.CharField(max_length=128)
    is_active= models.BooleanField()
    is_a_boy = models.BooleanField()
    is_staff = models.BooleanField()
    age = models.CharField(max_length=128)
    student_no = models.IntegerField(max_length=30)
        def set_password(password):
            return super().set_password(password)
        def check_password(password):
            return super().check_password(password)
        def save():
            return super().save()
from django.contrib.auth.models import AbstractUser, BaseUserManager
class yourCustomManager(BaseUserManager):
    def create_user(self, email, username=None, password=None, **extra_fields):
        if not email:
            raise ValueError('The Email field must be set')
        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, username =None, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        return self.create_user(email, password, **extra_fields)

What we have done here is simply just as it is. We have defined our method to create a normal user and one to create a super one, with the “is_staff” attribute default set to a “True” value. We also set passwords, so we will have a password to check in check_password.

Note that if you don't set your password using any of the methods above when you create a user, you won't be able to log in to the user. It will be hashed, but it won't be set.

The often encountered errors when creating custom user models are often of the nature of unset passwords, undefined username fields, migration dependencies, and database errors. This is one important reason to always create yourUserModel first before anything in the project and any migrations. Don't forget to define an “AUTH_USER_MODEL” in your settings.py should the use of the default “User” model no longer exist.

But oftentimes, the best technique to employ to evade these errors is making sure you define a custom manager when using “AbstractUser” and observe the following summary notes.

Summary Notes

  1. If your project is going to require you to customize a new User model, the model should be the first functionality to create before anything in your app to save you from difficult errors in the future.

  2. Fields that are set to “USERNAME_FIELD” and “EMAIL_FIELD” should not be included in the “REQUIRED_FIELDS” list.

  3. It is possible to set your “USERNAME_FIELD” to any field in your model that you would like to use to authenticate user object creation.

Note that any field set to “USERNAME_FIELD” or “EMAIL_FIELD” should be set to “unique=True."


I hope you have enjoyed this article so far and that you have gained a few insights on how Django's ORM works.

Thanks for reading.