In this article, we will go over the Django User Model. We can extend it with AbstractUser and AbstractBaseUser, but what options do we have? How can we customize it and how can we use it?
The Django User Model is part of the Django Authentication package. It provides you with a standard model that is used as a backbone for your user accounts. You can find the standard fields here.
There are various ways to extend the User
model with new fields. You might want to do this, when you want to add options to your users, or when you want to split them in groups (for when you have two different types of accounts for example).
With AbstractUser, you can overwrite the standard User model. It will inherit all functions and current fields from the standard User class and you can add anything you would like to this. Here is an example that would be in models.py
:
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.base_user import AbstractBaseUser
class User(AbstractBaseUser):
is_trailing = models.BooleanField(default=True)
# add extra fields here
Once you did that, you will also need to let Django know that we want to use that table as the default user table. You can do that with adding the next line to your settings.py
file (replace <yourappname>
with whatever your app name is.
AUTH_USER_MODEL = 'yourappname.User'
Why this is the preferred method: you keep your database extremely clean as you are not creating any extra unnecessary tables (always keep database normalisation in mind!). Remember that great programmers care much more about the design and architect (that's including the layout of your database), then the actual code. It's way easier to refactor code, then it is to migrate your database. It's very easy to create extra model functions for this as you have your user table in your models.
Alternatively, you could also use the AbstractBaseUser
. The difference with the AbstractUser
here is that AbstractBaseUser
doesn't have the standard fields and functions. In other words, you will have to add all the basic items yourself. For most people the AbstractUser
will be the right fit.
This works too, though you will not follow the normalisation rules that go for database tables. You will have to create an extra (unnecessary) table with this option. Extending the User model with an extra table is easy to implement though. Simply create the new table and then create a one-to-one field to the User model.
class Profile(models.Model):
user = models.OneToOneField('auth.User', on_delete=models.cascade)
# add extra fields here
Using this approach means that you will have to call fields through the User
class. Here is an example for in your template:
{% raw %}{{request.user.profile.bio}}{% endraw %}
As you can see, we first get the request, then the user, then the profile model that is attached to it and then the field we need. With the first method we can skip the profile
altogether as the fields are in the user model (note that profile is lowercase in this case!).
One thing you might want to consider with this method, is to immediately create your Profile
when you create the user. For this, we will have to trigger a function when a User is created. We can do that with Signals. Let's create one for the standard User class:
from django.contrib.auth.models import User
@receiver(post_save, sender=User)
def user_save(sender, instance, **kwargs):
Profile.objects.create(user=instance)
There are many cases were it's actually preferred to have users login with their email address instead of a username. A username is generally unnecessary for private applications. To remove the username, we will have to create a new User model and then assign a different UserManager
to it. The UserManager
is used to create new users. In this we will have to replace it with a custom one since we will not be creating new users like it is normally done. Here is an example:
class CustomUserManager(BaseUserManager):
def _create_user(self, email, password, is_staff, is_superuser, **extra_fields):
now = timezone.now()
if not email:
raise ValueError('The given email must be set')
email = self.normalize_email(email)
user = self.model(email=email, is_staff=is_staff, is_active=True, is_superuser=is_superuser, date_joined=now, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email, password=None, **extra_fields):
return self._create_user(email, password, False, False, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
return self._create_user(email, password, True, True,**extra_fields)
class User(AbstractBaseUser):
first_name = models.CharField(max_length=5000)
last_name = models.CharField(max_length=5000)
email = models.EmailField(max_length=5000, unique=True)
is_staff = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now_add=True)
last_login = models.DateTimeField(null=True)
# any fields you would like to add
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
As you can see, we are using the very bare-bone AbstractBaseUser
as we don't need the username field and can't change it with the AbstractUser
class. If we would have used the AbstractUser
class then we would still have to enter a username everywhere.
Stan is professional web developer working mainly with Django and VueJS. With years of experience under the belt, he is comfortable writing about his past mistakes and ongoing learnings.