Implementing Authentication for School Management System with Django
Table of contents
- Overview
- <mark>Learn about the Structure of Django Applications</mark> Here
- <mark>Learn About Django Models</mark> Here
- Defining Custom User model
- Defining Proxy model for different user types
- Creating UserCreationForm and UserChangeForm
- Updating core/settings.py and core/urls.py
- Defining URL paths and View Functions
- Registering model for Admin panel
- Conclusion
- <mark>Follow, like, share, comment and support</mark>
Overview
For the authentication system, we will start by defining our user model and then creating proxy models to differentiate between different types of users. We will also define custom managers for the models to override the create_user and get_queryset methods.
After defining our models we will be creating our custom user creation form and user change form to create and update our users. Then defining URL paths and writing view functions to process the form data and register different types of users.
For a better, understanding give the following blogs a read.
Learn about the Structure of Django Applications Here
Learn About Django Models Here
Defining Custom User model
We will be extending the AbstractBaseUser
from django.contrib.auth.models
and setting email as the username field for logging in users.
# authentication/models.py
from django.utils import timezone
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
# This the user manager with function to create_user and create_superuser methods. Manager is used to perform query operations
class UserManager(BaseUserManager):
def create_user(self, email, password=None):
if not email or len(email) <= 0:
raise ValueError("Email Field is required!!!!")
if not password:
raise ValueError("Password is required")
user = self.model(
email=self.normalize_email(email)
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password):
user = self.create_user(
email=self.normalize_email(email),
password=password
)
user.is_admin = True
user.is_staff = True
user.is_teacher = True
user.is_superuser = True
user.save(using=self._db)
return user
# This is the custom user model with type attribute for differentiating user types. Default type is student
class User(AbstractBaseUser,PermissionsMixin):
class Types(models.TextChoices):
STUDENT = "STUDENT", "student"
TEACHER = "TEACHER", "teacher"
type = models.CharField(
max_length=8, choices=Types.choices, default=Types.STUDENT)
email = models.EmailField(unique=True, max_length=100)
date_joined = models.DateTimeField(default=timezone.now)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_teacher = models.BooleanField(default=False)
is_student = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
objects = UserManager()
def __str__(self) -> str:
return str(self.email)
def has_perm(self, perm, obj=None) -> bool:
return self.is_admin
def has_module_perms(self, app_label) -> bool:
return True
def save(self, *args, **kwargs):
if not self.type or self.type == None:
self.type = User.Types.STUDENT
return super().save(*args, **kwargs)
Defining Proxy model for different user types
Proxy models operate on the same database table of concrete class in our case it is User but proxy models can have different behavior, permissions and meta options. We have used proxy models since both students and teachers should be able to login into the system.
# authentication/models.py
# Manager for student
class StudentManager(models.Manager):
def create_user(self, email, password=None):
if not email or len(email) <= 0:
raise ValueError("Email is Required!!")
if not password:
raise ValueError("Password is Required!!")
email = email.lower()
user = self.model(
email=self.normalize_email(email)
)
user.set_password(password)
user.save(using=self._db)
return user
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
queryset = queryset.filter(type=User.Types.STUDENT)
return queryset
# Student proxy model
class Student(User):
class Meta:
proxy = True
objects = StudentManager()
def save(self, *args, **kwargs):
self.type = User.Types.STUDENT
self.is_student = True
super().save(*args, **kwargs)
# Manager for Teacher model
class TeacherManager(models.Manager):
def create_user(self, email, password=None):
if not email or len(email) <= 0:
raise ValueError("Email Required!!")
if not password:
raise ValueError("Password is Required!!")
email = email.lower()
user = self.model(
email=self.normalize_email(email)
)
user.set_password(password)
user.save(using=self._db)
return user
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
queryset = queryset.filter(type=User.Types.TEACHER)
return queryset
# Teacher proxy model
class Teacher(User):
class Meta:
proxy = True
objects = TeacherManager()
def save(self, *args, **kwargs):
self.type = User.Types.TEACHER
self.is_teacher = True
self.is_staff = True
return super().save(*args, **kwargs)
For each proxy model, we have overridden the save method to change the attributes as per the proxy model and save the model
Creating UserCreationForm and UserChangeForm
Now we will extend the UserCreationForm and UserChangeForm and modify them as per our Proxy model. While defining a Form in Django we define the model in the inner class Meta along with fields to be exposed through the form.
# authentication/forms.py
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from authentication.models import Student, Teacher, User
# Creation form for our Concrete Model
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = User
fields = ('email',)
# Change form for updating User
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = User
fields = ('email',)
# Creation form for Teacher proxy model
class TeacherCreationForm(UserCreationForm):
class Meta:
model = Teacher
fields = ('email',)
# Change form for updating Teacher
class TeacherChangeForm(UserChangeForm):
class Meta:
model = Teacher
fields = ('email',)
# Creation form for Student proxy model
class StudentCreationForm(UserCreationForm):
class Meta:
model = Student
fields = ('email',)
# Change form for updating Student
class StudentChangeForm(UserChangeForm):
class Meta:
model = Student
fields = ('email',)
Updating core/settings.py and core/urls.py
The next step is to add our authentication app to the installed_apps list
# core/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# I have added authentication app here
'authentication',
]
# Settings to use our custom user for authentication
AUTH_USER_MODEL = 'authentication.User'
# core/urls.py
from django.contrib import admin
from django.urls import path, include
# Here we have included authentication urls to the main django project
# All path starting with 'auth/' will be looked up in authentication app urls file
urlpatterns = [
path('admin/', admin.site.urls),
path('auth/',include('authentication.urls'))
]
Defining URL paths and View Functions
Create a urls.py
file in the authentication
folder and add the following lines to it
# authentication/urls.py
from django.urls import path
from django.contrib.auth.views import LoginView
from . import views
# path( "path string/", views.function, name="optional name of path"
urlpatterns = [
path('',
views.homepage,
name="homepage"
),
path(
'login/',
views.loginUser,
name="login"
),
path(
'logout/',
views.logoutUser,
name="logout"
),
path(
'register-student/'
,views.registerStudent,
name="register-student"
),
path(
'register-teacher/',
views.registerTeacher,
name="register-teacher"
),
]
Now we will create view functions that are triggered as per the URL path that the client accesses in our web application.
# authentication/views.py
from django.shortcuts import redirect, render
from django.contrib import messages
from authentication.forms import StudentCreationForm, TeacherCreationForm
def homepage(request):
return render(request, "homepage.html")
def registerStudent(request):
if request.method=='POST':
form = StudentCreationForm(request.POST)
if form.is_valid():
form.save()
messages.success(request,'Registered Successfully')
else:
messages.error(request,'Please recheck the form data')
return redirect('register-student')
form = StudentCreationForm()
title = "Student Registration"
return render(request, 'authentication/register.html', {'form': form,'title':title})
def registerTeacher(request):
if request.method=='POST':
form = TeacherCreationForm(request.POST)
if form.is_valid():
form.save()
messages.success(request,'Registered Successfully')
else:
messages.error(request,'Please recheck the form data')
return redirect('register-teacher')
form = TeacherCreationForm()
title = "Teacher Registration"
return render(request, 'authentication/register.html', {'form': form,'title':title})
def loginUser(request):
if request.method=='POST':
email = request.POST['email']
password = request.POST['password']
user = authenticate(request,email=email,password=password)
if user is not None:
login(request,user)
messages.success(request,"Logged in Successfully")
return redirect('homepage')
else:
messages.error(request,"Invalid credentials!")
return render(request,'authentication/login.html')
def logoutUser(request):
logout(request)
return redirect('login')
For using the creation form we create an instance of the form and pass it to the template for rendering. We have created different functions for each user type and are passing different creation forms as per type to the template.
Registering model for Admin panel
Now finally we will be registering our models to the admin panel
# authentication/admin.py
from django.contrib import admin
from .models import *
admin.site.register(User)
admin.site.register(Teacher)
admin.site.register(Student)
Conclusion
We have almost completed the backend of the authentication system. We created our User models, proxy models for different user types, URL routes, and views functions for registering users, logging users in and logging them out.
In the next part, we will create templates i.e. the front end of our authentication system.