You are on page 1of 12

How to Create/Extend Django

Custom User Model


The Django build-in User model out of the box is solid. But it
sometimes might not suit your need since it has some predefined field
and you might want to add some more field to it like Date of Birth or
maybe making email-only authentication since Django has username-
password authentication out of the box. That ’s when Django ’s custom
user model comes handy.

Do I need a Custom User Model?


Most of the time you don’t. Django’s built-in User models provide more
than enough features to serve you out of the box. But if you want the
user to authenticate with an email address instead of username then
the custom model could be the right choice.

But that being said Django Official documentation suggests you have a
custom model even if you don’t want too so in future if you want to
add more information about the user you can do so which is more
likely if your app grows.

Application-specific data
The User model is meant to show information related to your user ’s
authentication or authentication point of view. You might even be
tempted to store other application-specific information related to
the user.

Since most users will have a profile picture, bio. Data like this
could be stored in another model which is referenced with an
OneToOneField with the User Model.

Storing application data on the User model


There are some advantages and disadvantages to storing application
data in the user model.

• For smaller applications, it will be simpler. There won ’t be many


things to look for. Everything in one place. But if the
application grows the same model can become clutter in no time.
• Since everything will be in one place there won ’t be a need for
advanced database queries. But that would also make your table
in the database bloated.

Storing application data in a profile model


The alternative way to store your user application data is to have
another model Profile which is referenced with OneToOneField to the
User Model.

• Since the application data is kept on another model the User


model is only used for authentication purposes and the Profile
model is used for user’s application data. Keeping it clutter-
free.
• But since the application data is different from authentication
data than to get the user application data we need to load
another object from the database in order to retrieve the data.
Depending on your application it could be worth the effort.
• The decoupling of application data with authentication data
makes it easier to make changes in the future.

Default Fields of User Model


The User Model Django provides out of the box has some fields in
general:

1.username: The username that the user will use to authenticate.


This will be unique in the table.
2.email: The email that the user can choose to share. But out-of-
the-box it is not unique.
3.password1: The password user chooses to authenticate. It is not
a plain password. It will be hashed for security reasons.
4.password2: This is used for confirm password. At the Admin side,
only one field named password is seen.
5.first_name: First name of the person.
6.last_name: Last name of the person.
7.last_login: The last Date and Time the user logged in.
8.date_joined: The Date and Time the user joined/authenticated
with your website.
9.groups: The groups the user is in.
10.user_permissions: Admin permissions if any.
11.is_staff: Designates whether the user can log into the admin
site.
12.is_active: Designates whether this user should be treated as
active. Unselect this instead of deleting accounts.
13.is_superuser: Designates that this user has all permissions
without explicitly assigning them.

Ways to Extend the User model


There are 4 ways to extend an existing User model in Django. Check
them below and choose the one that fits your needs.

1.Proxy Model
2.Profile Model with OneToOneField with User Model
3.AbstractBaseUser to Create a Custom User Model
4.AbstractUser to Create a Custom User Model

1. Proxy Model
Proxy models are a feature that developers might come across and move
along without giving much importance. They can come in handy in many
situations.

A proxy model is just another class that provides a different


interface for the same underlying database model.

A proxy model is actually a subclass of a database-table defining


model. Typically creating a subclass of a model results in a new
database table with a reference back to the original model ’s table ie.
multi-table inheritance.

A proxy model doesn’t get its own database table. Instead, it operates
on the original table.

It is used to change the behavior of an existing model (e.g. order,


new methods, etc.) without changing the existing database schema.

When to use Proxy Model?

When you don’t need to add extra data about but just want extra
methods or change the model’s query Manager.

It is a less intuitive way to extend the existing User model and is


limited in many ways. You won’t have any drawbacks with this strategy.
from django.contrib.auth.models import User
from .managers import PersonManager

class Person(User):
objects = PersonManager()

class Meta:
proxy = True
ordering = ('email', )

def do_something(self):
...

Let see what we did here.

• First, we defined a Proxy Model named Person. To tell Django


that it is a proxy model we make proxy = True in class Meta.
• We assign a custom Manager named PersonManager to the Proxy
model by objects = PersonManager( ).
• Change the default ordering to email.
• Defined a function do_something.

Also now User.objects.all( ) and Person.objects.all( ) will be


using the same database table to query. The only difference has been
the behavior of the Proxy Model.

2. Profile Model with OneToOne link with User Model


The idea behind this is to create another table in the database and
point the User model to that table in the database with
OneToOneField.

What is OneToOne Link?

It’s the Foreign Key relation but with unique=True. So every row in a
table would have only one relating row in another table and vice
versa.

But, when should I use this?

You should do this when you want to store profile related information
like a profile picture, bio, etc. Basically everything related to the
user that is not authentication specific.

Most of the time this is what you want.

Since we will be creating another model that stores user ’s profile


data that will be related to the User model. This will need an
additional query or join to access the related data. To access the
related data Django needs to fire additional queries.

So let’s see what to do here,


from django.db import models
from django.contrib.auth.models import User

class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.CharField(null=True, max_length=200)
birth_date = models.DateField(null=True, blank=True)
profile_pic = models.ImageField(default='default.jpg',
upload_to='profiles_pics')
hobby = models.CharField(null=True, max_length=200)

Now since the models are created, but they need to be created
whenever a user is created. We need to use signals to do the same.
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver

class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.CharField(null=True, max_length=200)
birth_date = models.DateField(null=True, blank=True)
profile_pic = models.ImageField(default='default.jpg',
upload_to='profiles_pics')
hobby = models.CharField(null=True, max_length=200)

@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()

Here, we are telling Django that whenever a save event occurs (signal
called post_save) create or save the profile depending on the
situation.

So, here whenever an entry/row of a user is created in the table, a


new entry/row is also created in the profile table in the database.

But that’s all fine, how do I access this profile data?

You can access this data in Django Templates like:


<h2>{{ user.username }}</h2>
<p>{{ user.email }}</p>
<p>{{ user.profile.bio }}</p>
<p>{{ user.profile.birth_date }}</p>

For using it in views:


def update_profile(request, user_id):
user = User.objects.get(pk=user_id)
user.profile.hobby = 'Coding, Chess, Guitar'
user.save()

Since we have signals setup we don ’t have to save the Profile models.
If we save the User model the Profile models will also be saved.

In Django, we can have multiple forms in the same templates at once.


So, if we want to have a profile page where users email, username and
other profile data like bio, etc is shown and can be updated by the
user.

First, we will create the forms: form.py


from django import forms
from django.contrib.auth.models import User
from .models import Profile

class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()

class Meta:
model = User
fields = ['username', 'email']

class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['profile_pic', 'bio']

To access the forms in views.py


@login_required
def profile(request):
if request.method == 'POST':
u_form = UserUpdateForm(request.POST, instance=request.user)
p_form = ProfileUpdateForm(request.POST, request.FILES,
instance=request.user.profile)

if u_form.is_valid() and p_form.is_valid():


u_form.save()
p_form.save()
messages.success(request, f'Your Profile has been
Updated Successfully')
return redirect('profile')
else:
u_form = UserUpdateForm(instance=request.user)
p_form = ProfileUpdateForm(instance=request.user.profile)
context = {
'u_form': u_form,
'p_form': p_form
}
return render(request, 'users/profile.html', context)

The template: profile.html


<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Profile Info</legend>
{{ u_form.as_p }}
{{ p_form.as_p }}
</fieldset>
<button type="submit">Update</button>
</form>
3. AbstractBaseUser to Create a Custom User Model
This method is like starting from a clean slate. Use this method if
you want to start from scratch.

This method will have no predefined field, it only contains the


authentication functionality, but no actual fields. You have to
supply them when you subclass.

It can be helpful in many cases like when you want to use email for
authentication instead of username. But there are better ways to do
the same by using AbstractUser instead of AbstractBaseUser .

You this at your own will. JK. You will be fine.

To demonstrate this we will be using the same example as in the


documentation.

Here is an example of an admin-compliant custom user app. This user


model uses an email address as the username, and has a required date
of birth; it provides no permission checking beyond an admin flag on
the user account.

This model would be compatible with all the built-in auth forms and
views, except for the user creation forms. This example illustrates
how most of the components work together, but is not intended to be
copied directly into projects for production use.

This code would all live in a models.py file for a custom


authentication app:
from django.db import models
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)

class MyUserManager(BaseUserManager):
def create_user(self, email, date_of_birth, password=None):
"""
Creates and saves a User with the given email, date of
birth and password.
"""
if not email:
raise ValueError('Users must have an email address')

user = self.model(
email=self.normalize_email(email),
date_of_birth=date_of_birth,
)

user.set_password(password)
user.save(using=self._db)
return user

def create_superuser(self, email, date_of_birth, password=None):


"""
Creates and saves a superuser with the given email, date of
birth and password.
"""
user = self.create_user(
email,
password=password,
date_of_birth=date_of_birth,
)
user.is_admin = True
user.save(using=self._db)
return user

class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
date_of_birth = models.DateField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)

objects = MyUserManager()

USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['date_of_birth']

def __str__(self):
return self.email

def has_perm(self, perm, obj=None):


"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True

def has_module_perms(self, app_label):


"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True

@property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin

Then, to register this custom user model with Django ’s admin, the
following code would be required in the app ’s admin.py file:
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField

from users.models import MyUser

class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation',
widget=forms.PasswordInput)

class Meta:
model = MyUser
fields = ('email', 'date_of_birth')

def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2

def save(self, commit=True):


# Save the provided password in hashed format
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user

class UserChangeForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
password hash display field.
"""
password = ReadOnlyPasswordHashField()

class Meta:
model = MyUser
fields = ('email', 'password', 'date_of_birth', 'is_active', 'is_admin')

def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial["password"]

class UserAdmin(BaseUserAdmin):
# The forms to add and change user instances
form = UserChangeForm
add_form = UserCreationForm

# The fields to be used in displaying the User model.


# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('email', 'date_of_birth', 'is_admin')
list_filter = ('is_admin',)
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Personal info', {'fields': ('date_of_birth',)}),
('Permissions', {'fields': ('is_admin',)}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'date_of_birth', 'password1', 'password2'),
}),
)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()

# Now register the new UserAdmin...


admin.site.register(MyUser, UserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)

Finally, specify the custom model as the default user model for your
project using the AUTH_USER_MODEL setting in your settings.py:
AUTH_USER_MODEL = 'users.MyUser'

4. AbstractUser to Create a Custom User Model


This is pretty simple to start with and is flexible too. AbstractUser
is your User Model the Django framework provides out of the box.
Since you will be subclassing it. So now you can change stuff inside
the class like using email field instead of username field for
authentications.

AbstractUser is a full User model, complete with fields, like an


abstract class, so that you can inherit from it and add your own
profile fields and methods.
from django.db import models
from django.contrib.auth.models import AbstractUser

class MyUser(AbstractUser):
bio = models.TextField(max_length=500, blank=True)
hobby = models.CharField(max_length=200, blank=True)

Update your setting.py to use the new User Model


AUTH_USER_MODEL = 'users.MyUser'

Where users is the app’s name

Django recommends having your own custom model even if you happy with
the out of the box User model.
What to do if I just need the default User Model and don ’t want to add
any other fields?

Do it like this:
from django.contrib.auth.models import AbstractUser

class MyUser(AbstractUser):
pass

def __str__(self):
return self.username

And obviously, tell Django to use your Custom User Model in


settings.py
AUTH_USER_MODEL = 'users.MyUser'

Referencing the User model
Now since you have changed your default User Model. You might need to
change the way you access it.

Now in Option 2 where we used OneToOneField in Profile Model to


relate to User model like this:
from django.db import models
from django.contrib.auth.models import User

class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
# Other Fields

After using our Custom Model, the right/better way is to use


get_user_model( ), like this:
from django.db import models
from django.contrib.auth import get_user_model

class Profile(models.Model):
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE)
# Other Fields

or
from django.db import models
from django.conf import settings

class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
# Other Fields

Up to you what you choose. Both will work fine.

Reusable apps and AUTH_USER_MODEL


Reusable apps shouldn’t implement a custom user model. A project may
use many apps, and two reusable apps that implemented a custom user
model couldn’t be used together. If you need to store per user
information in your app, use a ForeignKey or OneToOneField to
settings.AUTH_USER_MODEL as described above.
Right Choice?
It all ends up on you, your needs.

A project I am working now needed Email Authentication instead of the


username-password authentication that Django provides out of the box
and we wanted the username field to be removed from my User Model. So
we used AbstractUser to subclass my Custom User Model.

To store more information about Users like the profile picture, bio,
and other user-related information we created another Model called
Profile and we used OneToOneField to relate it to Custom User Model.

Conclusion
It’s always a good idea to use a custom user model if you are still
happy with the one provided by Django out of the box.

That’s all for Custom User Model in Django.

Let me know if this post helps you in any way in the comment section
below.

You might also like