Custom User Model and Authentication in Django
Django comes with authentication system out-of-the-box. But it may not be good enough for most common cases. For example, Django uses the username/email/password pattern for both signup and login. However, these days it is more common to simply use email/password. So, we need to have a custom user model. Otherwise, it’ll be very difficult to make any updates to the default user model.
Download complete project from GitHub.
Create Project
cd ~/Desktop
mkdir django_user
cd django_user
virtualenv --python=python3 venv
source venv/bin/activate
pip install django==2
django-admin startproject django_user
cd django_user
python manage.py runserver
Navigate to http://127.0.0.1:8000/ and we’ll see the Django welcome screen. Do not run migrate at this point.
Custom User Model
We will extend the existing User model to create our custom User model. It will have identical functionality but as it is custom, we will have the flexibility to make changes later on as needed.
Create a new app called users
:
python manage.py startapp users
Add it to INSTALLED_APPS
in settings.py
:
INSTALLED_APPS = [
...
'users',
]
Change the default user model for our custom User model. At the bottom of the settings.py
file, add the following line:
AUTH_USER_MODEL = 'users.CustomUser'
It tells Django that the user auth model to use is in our users app and called CustomUser
.
Model
Every model has a corresponding model manager. So, to make our own CustomUser
model, we’ll need to create both a new model and manager. We can create our own CustomUserManager
and CustomUser
by extending the model and manager used by Django for the default User. The existing model is called AbstractUser
and its corresponding model manager is UserManager
.
Update users/models.py
as follows:
from django.contrib.auth.models import AbstractUser, UserManager
class CustomUserManager(UserManager):
pass
class CustomUser(AbstractUser):
objects = CustomUserManager()
Here CustomUserManager
does nothing at this point except extend UserManager
, which means it contains all of UserManager’s code which we can modify later if needed. CustomUser
extends AbstractUser
and specifies that all its objects come from the CustomUserManager
.
Form
Now, we need to customize two forms: UserCreationForm
and UserChangeForm
. Create a new file in the users app called forms.py
and update it with the following code:
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = CustomUser
fields = UserCreationForm.Meta.fields
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = UserChangeForm.Meta.fields
Here, we created a new class called CustomUserCreationForm
that extends the existing UserCreationForm
. In CustomUserChangeForm
, we’re again subclassing an existing form UserChangeForm
and specifying it use our new CustomUser
model.
Admin
Update the admin.py
file to tell the admin about our new CustomUser
model. Update the existing UserAdmin
class:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .forms import CustomUserCreationForm, CustomUserChangeForm
from .models import CustomUser
class CustomUserAdmin(UserAdmin):
model = CustomUser
add_form = CustomUserCreationForm
form = CustomUserChangeForm
admin.site.register(CustomUser, CustomUserAdmin)
Here, we created a new class CustomUserAdmin
and specified it to use the new model and forms.
Run makemigrations and migrate for the first time to create a new database that uses the custom user model.
python manage.py makemigrations
python manage.py migrate
Create a superuser account:
python manage.py createsuperuser
If it works then our custom user model works as expected.
User Authentication
Django provides everything for login and logout but we will create our own form to sign up new users.
Templates
Create a new templates directory in project root directory and within it a registration folder as that’s where Django will look for the login template.
mkdir templates
mkdir templates/registration
Update the configuration for ‘DIRS’ in settings.py
:
'DIRS': [
os.path.join(BASE_DIR, 'templates'),
],
Add these two lines at the bottom of the settings.py file:
LOGIN_REDIRECT_URL = 'home'
LOGOUT_REDIRECT_URL = 'home'
These two lines tell Django where to redirect after login or logout of a site. In this case, we want to redirect to our homepage.
Now, create four new templates. The base.html will be inherited by every other template in our project.
templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> Custom User Model </title>
</head>
<body>
{{ "{% block content" }} %}
{{ "{% endblock" }} %}
</body>
</html>
templates/home.html
{{ "{% extends 'base.html'" }} %}
{{ "{% block title" }} %} Home {{ "{% endblock" }} %}
{{ "{% block content" }} %}
{{ "{% if user.is_authenticated" }} %}
Welcome {{ "{{ user.username" }} }}!
{{ '<p><a href="{%' }} url 'logout' %}"> Logout </a></p>
{{ "{% else" }} %}
<p>You are not logged in</p>
{{ '<a href="{% url' }} 'login' %}"> Login </a> | {{ '<a href="{% url' }} 'signup' %}"> Signup </a>
{{ "{% endif" }} %}
{{ "{% endblock" }} %}
templates/registration/login.html
{{ "{% extends" }} 'base.html' %}
{{ "{%" }} block title %} Login {{ "{%" }} endblock %}
{{ "{%" }} block content %}
<h2> Login </h2>
<form method="post">
{{ "{%" }} csrf_token %}
{{ "{{" }} form.as_p }}
<button type="submit"> Login </button>
</form>
{{ "{% endblock" }} %}
templates/signup.html
{{ "{% extends" }} 'base.html' %}
{{ "{%" }} block title %} Sign Up {{ "{% endblock" }} %}
{{ "{%" }} block content %}
<h2> Sign Up </h2>
<form method="post">
{{ "{%" }} csrf_token %}
{{ "{{" }} form.as_p }}
<button type="submit"> Sign up </button>
</form>
{{ "{% endblock" }} %}
Views
Update users/views.py
which will contain the logic for signup form. We’re using Django’s generic CreateView and telling it to use our custom form_class, to redirect to login once a user signs up successfully, and that the template is named signup.html.
from django.urls import reverse_lazy
from django.views import generic
from .forms import CustomUserCreationForm
class SignUp(generic.CreateView):
form_class = CustomUserCreationForm
success_url = reverse_lazy('login')
template_name = 'signup.html'
URLs
Update django_user/urls.py
:
from django.contrib import admin
from django.urls import path, include
from django.views.generic.base import TemplateView
urlpatterns = [
path('', TemplateView.as_view(template_name='home.html'), name='home'),
path('admin/', admin.site.urls),
path('users/', include('users.urls')),
path('users/', include('django.contrib.auth.urls')),
]
Create a urls.py
file in the users app:
from django.urls import path
from . import views
urlpatterns = [path('signup/', views.SignUp.as_view(), name='signup'), ]
Start the server and go to the homepage.
Sign up a new user.
Login with the new user.
After login, the user will be redirected to the personalized homepage.
For custom authentication process, visit Customizing authentication in Django.