Django's Authentication and Authorisation

ยท

5 min read

User registration and authentication can be a tricky process in development for many developers, and especially beginners.

In this article, I'll take you step by step on how to go about the same in Django. This project's code can be found on GitHub

# This tutorial assumes you have Python and Django installed, and have basic understanding of the two.

To start navigate to your preferred directory where you will store your Django project with:

# In your cmd
cd <my_directory>

Run the commands:

django-admin startproject <project_name> .
django-admin startapp <app_name>

In your preferred code editor, navigate to your project's directory. We can start coding our system; open the settings.py file in the project directory and add the name of our app in the INSTALLED APPS setting.

# Application definition

INSTALLED_APPS = [
    'jazzmin', # (optional)
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'name_of_the_app', # replace with the name of the app you created
]

Open the models.py file in your app and write the code below:

# models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser,  BaseUserManager, PermissionsMixin

We are importing three modules; AbstractBaseUser BaseUserManager PermissionsMixin. These will be extended from when creating our user models.

Let's define our first model:

class UserManager(BaseUserManager):
    def create_user(self, email, first_name, last_name, password=None):
        if not email:
            raise ValueError("You need to provide an email to register!")
        if not first_name:
            raise ValueError("You must provide your last name!")
        if not last_name:
            raise ValueError("You must provide your last name!")

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

    def create_superuser(self, email, first_name, last_name, password=None):
        user = self.create_user(
            email = self.normalize_email(email),
            first_name=first_name,
            last_name = last_name,
            password = password,
        )
        user.is_admin = True
        user.is_active = True
        user.is_staff = True
        user.is_superuser = True
        user.save(using=self._db)
        return user

We can then define the model for our user, and the fields we prefer them to have:

class RegularUser(AbstractBaseUser,  PermissionsMixin):
    id = models.AutoField(primary_key=True)
    email = models.EmailField(unique=True)
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

    is_admin = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

    date_joined = models.DateTimeField(auto_now_add=True, null=True)
    last_login = models.DateTimeField(auto_now_add=True, null=True)
    created_date = models.DateTimeField(auto_now_add=True, null=True)
    modified_date = models.DateTimeField(auto_now_add=True, null=True)

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

    objects = UserManager()

    def __str__(self):
        return f"{self.first_name} {self.last_name}"

    def has_perm(self, perm, obj=None):
        return self.is_admin

    def has_module_perms(self, app_label):
        return True

Run our app's migrations by running:

# Windows
python manage.py makemigrations
python manage.py migrate

In our settings.py file, add this code at the bottom:

AUTH_USER_MODEL = '<name_of_your_app>.RegularUser'

AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend']

Then comment out this part of code in the same file:

# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators

# AUTH_PASSWORD_VALIDATORS = [
#     {
#         'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
#     },
#     {
#         'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
#     },
#     {
#         'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
#     },
#     {
#         'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
#     },
# ]

Create a forms.py file in the same directory as the models.py file:

from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import RegularUser


class SignUpForm(UserCreationForm):
    email = forms.EmailField()

    class Meta():
        model = RegularUser
        fields = [
            'email',
            'first_name',
            'last_name',
            'password1',
            'password2',
        ]      


class LoginForm(forms.Form):
    email = forms.EmailField()
    password = forms.CharField(widget=forms.PasswordInput())

In the views.py file, include this code:

from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth import login, logout, authenticate
from .forms import SignUpForm, LoginForm
from django.contrib import messages


@login_required
def homepage(request):
        user = request.user
        first_name = request.user.first_name
        last_name = request.user.last_name

        context = {
            'user': user,
            'first_name': first_name,
            'last_name': last_name,
        }
        return render(request, "homepage.html", context)



def signup_view(request):
    if request.method == 'POST':
        form = SignUpForm(request.POST)
        if form.is_valid():
            user = form.save()
            user.save()
            return redirect("login")
    else:
        form = SignUpForm()
    return render(request, "registration/signup.html", {'form': form})


def login_view(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            email = form.cleaned_data['email']
            password = form.cleaned_data['password']

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

            if user is not None:
                login(request, user)
                return redirect("dashboard")
            else:
                messages.error(request, "Invalid login credentials!")
    else:
        form = LoginForm()
    return render(request, "registration/login.html", {'form': form})



@login_required
def logout_view(request):
    logout(request)
    messages.success(request, "You have been logged out!")
    return redirect('login')

Create another file; urls.py in the same directory as the views.py and include this code:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.homepage, name="dashboard"),
    path('signup/', views.signup_view, name="signup"),
    path('login/', views.login_view, name="login"),
    path('logout/', views.logout_view, name="logout"),
]

In the app directory, create a folder named templates alongside a file named homepage.html with this code:

<!DOCTYPE html>
<html>
    <head>
        <title>Dashboard</title>
    </head>
    <body>
        <p>Hey {{ user.first_name }} {{ user.last_name }}!</p>
        <h2>You are viewing the dashboard</h2>
    </body>
</html>

In the templates directory, create another directory named registration and create two HTML files:

signup.html

# signup.html
<!DOCTYPE html>
<html>
    <head>
        <title>Sign Up</title>
    </head>
    <body>
        <h1>Sign Up</h1>
        <form method="post" enctype="multipart/form-data">
            {% csrf_token %}
            {{ form.as_p }}
            <button type="submit">Register</button>
        </form>
    </body>
</html>

login.html

# login.html
<!DOCTYPE html>
<html>
    <head>
        <title>Login Page</title>
    </head>
    <body>
        <h1>Log In</h1>
        <form method="post" enctype="multipart/form-data">
            {% csrf_token %}
            {{ form.as_p }}
            <button type="submit">Log In</button>
        </form>
    </body>
</html>

Open the urls.py file in the project directory, and modify the code to include:

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('<app_name>.urls')),
]

Save all the files and run the server with:

# Windows
python manage.py runserver

Fire up your browser and navigate to http://127.0.0.1:8000/.

Users can signup for an account, and log in to your app!

*This article will continue to be updated for the better and ease of read*

ย