Search Here

Django Creating Custom User Model with Custom Authentication Backend

Django Creating Custom User Model with Custom Authentication Backend

Table of Contents

Setup Project

Create a new project by typing command django-admin startproject user_model_test. user_model_test is just a project you can give it any other name you like.

Create App

  • Go inside root directory and in terminal django-admin startapp custom_user this command creates a new app for the project.
  • In project settings.py file installed app section register newly created app.
INSTALLED_APPS = [
	'...',
	'custom_user.apps.CustomUserConfig',
]

In settings.py specify database connection.

DATABASES = {
	'default': {
		'ENGINE': 'django.db.backends.mysql',
		'NAME': 'user_model_test',
		'USER': 'root',
		'PASSWORD': '....',
		'HOST': 'localhost',   # Or an IP Address that your DB is hosted on
		'PORT': '3306',
		'OPTIONS': {
			'sql_mode': 'traditional',
		}
	}
}

Create a Customer Manager for Model that extends BaseUserManager and Custom Model that extends AbstractBaseUser

Open models.py file located in app custom_user. Create a custom user model that will replace the default authentication, user model.

from django.db import models
from django.contrib.auth.models import (
	BaseUserManager, AbstractBaseUser, AbstractUser
)

# Create your models here.
class CustomUserManager(BaseUserManager):
	def create_user(self, password, first_name, phone=None, last_name=None):
		if not phone:
			raise ValueError("User must have phone number")

		user = self.model(
			first_name = first_name,
			last_name = last_name,
			phone = phone
		)
		user.set_password(password)
		user.save(using=self._db)
		return user

	def create_superuser(self, password, first_name, phone=None, last_name=None):
		user = self.create_user(
			first_name = first_name,
			last_name = last_name,
			phone = phone,
			password = password
		)
		user.is_admin=True
		user.save(using=self._db)
		return user
		
class CustomUser(AbstractBaseUser):

	id = models.AutoField(primary_key=True)
	first_name = models.CharField(max_length=150)
	last_name = models.CharField(max_length=150,null=True)
	email = models.EmailField(null=True)
	phone = models.CharField(max_length=150,unique=True)
	password = models.CharField(max_length=150)
	is_active = models.BooleanField(default=True)
	is_admin = models.BooleanField(default=False)
	created_at = models.DateTimeField(auto_now_add=True)
	updated_at = models.DateTimeField(auto_now=True)

	USERNAME_FIELD = 'phone'
	REQUIRED_FIELDS = ['first_name', 'last_name']

	objects = CustomUserManager()

	def __str__(self):
		return "{} {}".format(self.phone, self.first_name)

	@property
	def is_staff(self):
		return self.is_admin

	@property
	def is_anonymous(self):
		return False

	@property
	def is_authenticated(self):
		return True

	class Meta():
		db_table = 'auth_user'
		verbose_name = 'User'
		verbose_name_plural = 'Users'

The CustomUser model inherits AbstractBaseUser. We can specify all the important fields we require in our table.

In USERNAME_FIELD you have specified phone which means that we’ll be using phone number to authenticate the user and log him into the system.
The REQUIRED_FIELDS are necessary to assign as this will be asked by system when creating superuser.

For the custom user model, it is mandatory to specify Custom Manager and assign it to objects variable objects = CustomUserManager() with this, you can fetch objects from the model.

CustomUser.objects.create_user(**kwargs)
#OR
CustomUser.objects.create_superuser(**kwargs)

It is also important to specify property such are is_staff, is_anonymous, is_authenticated these will be used by Django backend and also by us.

The Manager CustomUserManager inherits BaseUserManager and here we need to add two main methods create_user and create_superuser.

The Django Backend will make use of create_superuser method while registering a new superuser.

Change Default AUTH_USER_MODEL in settings

Django does not know we have created a model that has to be used for authentication. To make Django use or custom model for authentication we must specify a path to our model in settings.py file.
In settings.py file add the below path.

AUTH_USER_MODEL = 'custom_user.CustomUser'

AUTH_USER_MODEL = ‘.’.

Create Custom Authentication Backend

In-app custom_user create backend.py it is in this file which you specify how to or custom way of user authentication.
We’ll return user object if phone and password matches else return None.

from django.conf import settings
from django.contrib.auth.hashers import check_password
from custom_user.models import CustomUser

class CustomUserBackend:
	
	def authenticate(self,request,phone=None,password=None):
		if phone and password:
			try:
				user = CustomUser.objects.get(phone=phone)
				if check_password(password,user.password):
					if user.is_active:
						return user
			except CustomUser.DoesNotExist:
				return None
		return None

	def get_user(self,user_id):
		try:
			return CustomUser.objects.get(pk=user_id)
		except CustomUser.DoesNotExist:
			return None

In class CustomUserBackend it is mandatory to specify authenticate() and get_user() method.

In authenticate() we pass arguments such as request the object is mandatory as the authenticated user data will be stored their phone and password for verification.

Change Default AUTHENTICATION_BACKENDS to newly created one

We are just a step behind. Now we have created CustomUserBackend we’ll inform Django to use this backend insisted on default.
To do that go to settings.py file and add the below code.

AUTHENTICATION_BACKENDS = ['custom_user.backend.CustomUserBackend','django.contrib.auth.backends.ModelBackend']

User Logging example

In order to make you completely understand below, we have an example of a user logging system with all codes.
In custom_user/models.py

from django.db import models
from django.contrib.auth.models import (
	BaseUserManager, AbstractBaseUser, AbstractUser
)

# Create your models here.
class CustomUserManager(BaseUserManager):
	def create_user(self, password, first_name, phone=None, last_name=None):
		if not phone:
			raise ValueError("User must have phone number")

		user = self.model(
			first_name = first_name,
			last_name = last_name,
			phone = phone
		)
		user.set_password(password)
		user.save(using=self._db)
		return user

	def create_superuser(self, password, first_name, phone=None, last_name=None):
		user = self.create_user(
			first_name = first_name,
			last_name = last_name,
			phone = phone,
			password = password
		)
		user.is_admin=True
		user.save(using=self._db)
		return user


class CustomUser(AbstractBaseUser):

	id = models.AutoField(primary_key=True)
	first_name = models.CharField(max_length=150)
	last_name = models.CharField(max_length=150,null=True)
	email = models.EmailField(null=True)
	phone = models.CharField(max_length=150,unique=True)
	password = models.CharField(max_length=150)
	is_active = models.BooleanField(default=True)
	is_admin = models.BooleanField(default=False)
	created_at = models.DateTimeField(auto_now_add=True)
	updated_at = models.DateTimeField(auto_now=True)

	USERNAME_FIELD = 'phone'
	REQUIRED_FIELDS = ['first_name', 'last_name']

	objects = CustomUserManager()

	def __str__(self):
		return "{} {}".format(self.phone, self.first_name)

	@property
	def is_staff(self):
		return self.is_admin

	@property
	def is_anonymous(self):
		return False

	@property
	def is_authenticated(self):
		return True

	class Meta():
		db_table = 'auth_user'
		verbose_name = 'User'
		verbose_name_plural = 'Users'

In custom_user/backend.py.

from django.conf import settings
from django.contrib.auth.hashers import check_password
from custom_user.models import CustomUser

class CustomUserBackend:
	
	def authenticate(self,request,phone=None,password=None):
		if phone and password:
			try:
				user = CustomUser.objects.get(phone=phone)
				if check_password(password,user.password):
					if user.is_active:
						return user
			except CustomUser.DoesNotExist:
				return None
		return None

	def get_user(self,user_id):
		try:
			return CustomUser.objects.get(pk=user_id)
		except CustomUser.DoesNotExist:
			return None

In settings.py file

AUTH_USER_MODEL = 'custom_user.CustomUser'
AUTHENTICATION_BACKENDS = ['custom_user.backend.CustomUserBackend','django.contrib.auth.backends.ModelBackend']

In urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
	path('admin/', admin.site.urls),
	path('custom_user/', include('custom_user.urls')),
]

In custom_user/urls.py

from django.urls import path, re_path
from custom_user import views

app_name="custom_user"

urlpatterns = [
	path('login', views.log_in, name="login"),
	path('login/verify', views.verify_login, name="login-verify"),
	path('logout', views.log_out, name="logout"),
	path('home', views.home, name="home"),
]

In custom_user/views.py

from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.urls import reverse

# Create your views here.
def log_in(request):
	template="custom_user/login.html"
	context={}
	return render(request,template,context)

def verify_login(request):
	phone = request.POST['phone']
	password = request.POST['password']

	user = authenticate(request, phone=phone, password=password)

	if user is not None:
		login(request, user)
		# Redirect to a success page.
		return redirect(reverse("custom_user:home"))
	else:
		return redirect(reverse("custom_user:login"))

def log_out(request):
	logout(request)
	return redirect(reverse("custom_user:login"))

@login_required(login_url='custom_user:login')
def home(request):
	template="custom_user/home.html"
	context={}
	return render(request,template,context)

In custom_user/templates/custom_user/login.html

<h2>Login</h2>

<form action="{% url 'custom_user:login-verify' %}" method="post">
	{% csrf_token %}
	<label for="">Phone No*</label>
	<input type="text" name="phone" value="">
	<hr>
	<label for="">Password*</label>
	<input type="password" name="password" value="">
	<hr>
	<input type="submit" value="Login">
</form>

In custom_user/templates/custom_user/home.html

<h1>Welcome to Home</h1>
<a href="{% url 'custom_user:logout' %}">Logout</a>

Conclusion

We have come to the final part of our post on Django Creating Custom User Model with Custom Authentication Backend. For any suggestions and queries comment below.

Recent Posts

Summary
Review Date
Reviewed Item
Django Creating Custom User Model with Custom Authentication Backend
Author Rating
51star1star1star1star1star
Software Name
Django Framework
Software Name
Windows Os, Mac Os, Ubuntu Os
Software Category
Web Development