How to Create/Extend Django Custom User Model
Django Documentation says to create a Django custom user model at the start of the project. But it can look like a daunting task. Let's look at how to do it easily.
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 fields 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 in handy.
Django uses Python language to code the logic.
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 a 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 is 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 theProfile
model is used for the 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:
username
: The username that the user will use to authenticate. This will be unique in the table.email
: The email that the user can choose to share. But out-of-the-box it is not unique.password1
: The password user chooses to authenticate. It is not a plain password. It will be hashed for security reasons.password2
: This is used to confirm passwords. On the Admin side, only one field named password is seen.first_name
: The first name of the person.last_name
: The last name of the person.last_login
: The last Date and Time the user logged in.date_joined
: The Date and Time the user joined/authenticated with your website.groups
: The groups the user is in.user_permissions
: Admin permissions if any.is_staff
: Designates whether the user can log into the admin site.is_active
: Designates whether this user should be treated as active. Unselect this instead of deleting accounts.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.
- Proxy Model
Profile
Model withOneToOneField
withUser
ModelAbstractBaseUser
to Create a CustomUser
ModelAbstractUser
to Create a CustomUser
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 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.
In models.py
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
inclass Meta
. - We assign a custom Manager named
PersonManager
to the Proxy model byobjects = 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 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,
In models.py
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.
models.py
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:
template.html
<h2>{{ user.username }}</h2>
<p>{{ user.email }}</p>
<p>{{ user.profile.bio }}</p>
<p>{{ user.profile.birth_date }}</p>
For using it in views.py
:
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 set up 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 in 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 a username. But there are better ways to do the same by using AbstractUser
instead of AbstractBaseUser
.
You do 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 are 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.
models.py
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 are happy with the out of 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 in models.py
:
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
a model like this:
models.py
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:
models.py
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
models.py
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 on 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.