14. User Authentication Tutorial#

  • Reference

  • Django provides a robust authentication system that handles user accounts, groups, permissions, and cookie-based user sessions.

  • In this tutorial, we will learn how to create an authentication system for a Django application. This involves setting up user registration, login, and logout functionality, and restricting access to certain pages.

14.1. Set up User Authentication app#

14.1.1. Register app#

  1. First, create a new app named users:

    python manage.py startapp users
    
  2. Add the app to INSTALLED_APPS in django_project/settings.py:

    INSTALLED_APPS = [
        "users.apps.UsersConfig",
        ...
    ]
    

14.2. Create User Models and Forms#

This section focuses on defining the user profile model and creating forms for user registration and profile updates.

14.2.1. Setup Crispy environment#

  1. install the required libraries

    pip install django-crispy-forms
    pip install crispy-bootstrap4
    
  2. Add the app to INSTALLED_APPS in django_project/settings.py:

    INSTALLED_APPS = [
        "crispy_forms",
        "crispy_bootstrap4",
        ...
    ]
    
  3. Set up the crispy environment in django_project/settings.py:

    CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap4"
    CRISPY_TEMPLATE_PACK = 'bootstrap4'
    

14.2.2. Create forms in users/forms.py#

  • Since User is a build-in model in django.contrib.auth, we do not need to construct our own model again.

  • Meta Class: The Meta class is used to specify which model the form is associated with (User) and which fields should be included in the form.

from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import UserProfile
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit

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

    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2']
  • Here is a table listing the supported fields in Django forms along with a brief description and an example

Field Type

Description

Example

CharField

A field for small- to large-sized strings.

name = forms.CharField(max_length=100)

EmailField

A field for email addresses.

email = forms.EmailField()

IntegerField

A field for integer values.

age = forms.IntegerField()

FloatField

A field for floating-point numbers.

price = forms.FloatField()

BooleanField

A field for boolean values.

is_active = forms.BooleanField()

DateField

A field for date values.

birth_date = forms.DateField()

DateTimeField

A field for date and time values.

created_at = forms.DateTimeField()

TimeField

A field for time values.

start_time = forms.TimeField()

URLField

A field for URL values.

website = forms.URLField()

SlugField

A field for slug values.

slug = forms.SlugField()

ChoiceField

A field for selecting a choice from a set of options.

choice = forms.ChoiceField(choices=[('1', 'Option 1'), ('2', 'Option 2')])

MultipleChoiceField

A field for selecting multiple choices from a set of options.

choices = forms.MultipleChoiceField(choices=[('1', 'Option 1'), ('2', 'Option 2')])

FileField

A field for uploading files.

file = forms.FileField()

ImageField

A field for uploading image files.

image = forms.ImageField()

DecimalField

A field for decimal values with fixed precision.

price = forms.DecimalField(max_digits=10, decimal_places=2)

DurationField

A field for storing durations of time.

duration = forms.DurationField()

UUIDField

A field for storing universally unique identifiers.

uuid = forms.UUIDField()

JSONField

A field for storing JSON-encoded data.

metadata = forms.JSONField()

ModelChoiceField

A field for selecting a single model instance.

author = forms.ModelChoiceField(queryset=Author.objects.all())

ModelMultipleChoiceField

A field for selecting multiple model instances.

authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())

RegexField

A field for validating input against a regular expression.

zip_code = forms.RegexField(regex=r'^\d{5}$')

IPAddressField

A field for validating IPv4 addresses.

ip_address = forms.IPAddressField()

GenericIPAddressField

A field for validating IPv4 and IPv6 addresses.

ip_address = forms.GenericIPAddressField(protocol='both')

TypedChoiceField

A field like ChoiceField, but returns a value of a specific type.

choice = forms.TypedChoiceField(choices=[('1', 'Option 1'), ('2', 'Option 2')], coerce=int)

TypedMultipleChoiceField

A field like MultipleChoiceField, but returns values of a specific type.

choices = forms.TypedMultipleChoiceField(choices=[('1', 'Option 1'), ('2', 'Option 2')], coerce=int)

SplitDateTimeField

A field for entering date and time separately.

appointment = forms.SplitDateTimeField()

ComboField

A field that combines multiple fields into one.

combo = forms.ComboField(fields=[forms.CharField(max_length=10), forms.EmailField()])

MultiValueField

A field that validates multiple values.

multi = forms.MultiValueField(fields=[forms.CharField(), forms.CharField()])

SplitHiddenDateTimeField

A field like SplitDateTimeField, but renders as hidden widgets.

hidden = forms.SplitHiddenDateTimeField()

14.3. Create Views for User Authentication#

14.3.1. Create register view#

  • If request.method == POST, we get the POST data and check the validation, if passed, we use form.save to push data into our database.

  • If it’s a GET request, we just render the register.html template

    from django.contrib import messages
    from .forms import UserRegisterForm
    
    # Create your views here.
    def register(request):
        if request.method == 'POST':
            form = UserRegisterForm(request.POST)
    
            if form.is_valid():
                form.save()
                username = form.cleaned_data.get('username')
                messages.success(request, f'Account created for {username}!')
                return redirect('blog-home')
        else:
            form = UserRegisterForm()
        return render(request, 'users/register.html', {'form': form})
    
    
  • Django's messages framework allows you to store messages in one request and retrieve them for display in a subsequent request. It’s often used to provide feedback to the user.

    • Example in Views

      # Example in a view
      messages.success(request, 'Your message here')
      
    • Handling Messages in Templates:

      {% for message in messages %}
      <div class="alert alert-{{ message.tags }}">
          {{ message }}
      </div>
      {% endfor %}
      
    • Message Levels:

      • messages.debug

      • messages.info

      • messages.success

      • messages.warning

      • messages.error

14.3.2. Setting Up URL Patterns#

  1. Define URLs in users/urls.py:

    from django.urls import path
    from . import views
    
    urlpatterns = [
        path('register/', views.register, name='register'),
        # path('profile/', views.profile, name='profile'),
    ]
    
  2. Include users URLs in your project’s django_project/urls.py:

    urlpatterns = [
        ...,
        path('users/', include('users.urls')),
        ...,
    ]
    

14.3.3. Creating Templates#

  • Create register.html in users/templates/users/register.html:

    {% extends "blog/main.html" %}
    {% load crispy_forms_tags %}
    
    {% block main-content %}
    
    <div class="container">
        <h2>Join Today</h2>
        {% for message in messages %}
        <div class="alert alert-{{ message.tags }}">
            {{ message }}
        </div>
        {% endfor %}
        <form method="POST">
            {% csrf_token %}
            {{ form|crispy }}
            <div class="d-flex justify-content-end">
                <button type="submit" class="btn btn-primary me-2">Register</button>
                <a href="{% url 'blog-home' %}" class="btn btn-danger">Cancel</a>
            </div>
        </form>
        <div class="border-top mt-3">
            <small class="text-muted">
                Already Have An Account? <a class="ml-2" href="#">Sign In</a>
            </small>
        </div>
    </div>
    
    {% endblock main-content %}
    
  • Button Alignment: The d-flex justify-content-end class places both buttons on the right side of the form row.

  • Button Spacing: The me-2 class adds a margin to the right of the Register button, creating space between the buttons.

  • Cancel Button: The cancel button is styled as a danger button (btn btn-danger) to differentiate it from the primary action.

14.4. Login and Logout Page#

14.4.1. Step 1: Construct the URL Patterns#

  • First, you’ll need to define the URL patterns for the login and logout views using Django’s built-in views.

    # users/urls.py
    from django.urls import path
    from django.contrib.auth.views import LoginView, LogoutView
    
    urlpatterns = [
        ...
        path('login/', LoginView.as_view(template_name='users/login.html'), name='login'),
        path('logout/', LogoutView.as_view(template_name='users/logout.html'), name='logout'),
        ...
    ]
    
    
  • LoginView is used to handle user login. It displays a login form and processes the login request.

Parameter

Description

Default Value

Example Value

template_name

The name of the template to use for displaying the login form.

'registration/login.html'

'users/login.html'

authentication_form

The form class to use for validating the login.

AuthenticationForm

MyCustomAuthenticationForm

redirect_authenticated_user

Whether to redirect authenticated users who access the login page.

False

True

extra_context

A dictionary of context data to add to the default context.

{}

{'next': 'somewhere'}

redirect_field_name

The name of a GET parameter that contains the URL to redirect to after login.

'next'

'next_page'

success_url

URL to redirect to after a successful login.

None

'/profile/'

  • LogoutView is used to handle user logout. It logs the user out and redirects them to a specified URL.

Parameter

Description

Default Value

Example Value

template_name

The name of the template to use for displaying a post-logout message (if any).

None

'users/logout.html'

next_page

URL to redirect to after logging out.

None

'/login/'

redirect_field_name

The name of a GET parameter that contains the URL to redirect to after logout.

'next'

'next_page'

extra_context

A dictionary of context data to add to the default context.

{}

{'info': 'You have been logged out.'}

14.4.2. Step 2: Create the Templates for Login and Logout Pages#

  • Next, create the templates for the login and logout pages. Use Django’s template inheritance and crispy forms for better styling.

  • users/templates/users/login.html

    {% extends "blog/main.html" %} {% load crispy_forms_tags %} {% block
    main-content %}
    <div class="container">
      <h2>Log In</h2>
      {% for message in messages %}
      <div class="alert alert-{{ message.tags }}">{{ message }}</div>
      {% endfor %}
      <form method="POST">
        {% csrf_token %} {{ form|crispy }}
        <div class="d-flex justify-content-end">
          <button type="submit" class="btn btn-primary me-2 mt-2">Login</button>
          <a href="{% url 'blog-home' %}" class="btn btn-danger mt-2">Cancel</a>
        </div>
      </form>
      <div class="border-top mt-3">
        <small class="text-muted">
          Need an account?
          <a class="ml-2" href="{% url 'register' %}">Sign Up Now</a>
        </small>
      </div>
    </div>
    {% endblock main-content %}
    
  • users/templates/users/logout.html

    {% extends "blog/main.html" %} {% block main-content %}
    <div class="container">
    <h2>You have been logged out</h2>
    <a href="{% url 'login' %}" class="btn btn-primary mt-2">Log In Again</a>
    </div>
    {% endblock main-content %}
    

14.4.3. Step 3: Add Conditional Statements in the Navigation Bar#

  • Update your navigation bar template to show different options based on the user’s authentication status.

    <!-- Navbar template -->
    ...
    <div class="navbar-nav ml-auto">
      {% if user.is_authenticated %}
      <a class="nav-item nav-link" href="{% url 'profile' %}">Profile</a>
      <a class="nav-item nav-link" href="{% url 'logout' %}">Logout</a>
      {% else %}
      <a class="nav-item nav-link" href="{% url 'login' %}">Login</a>
      <a class="nav-item nav-link" href="{% url 'register' %}">Register</a>
      {% endif %}
    </div>
    ...
    
  • In Django, the user object, typically an instance of django.contrib.auth.models.User, has various attributes and methods that provide information about the user and enable interaction with user data.

14.4.3.1. Attributes (Parameters)#

Attribute

Purpose

Example Usage

username

The username of the user.

{{ user.username }}

first_name

The first name of the user.

{{ user.first_name }}

last_name

The last name of the user.

{{ user.last_name }}

email

The email address of the user.

{{ user.email }}

is_staff

True if the user is allowed to access the admin site.

{{ user.is_staff }}

is_active

True if the user account is active.

{{ user.is_active }}

is_superuser

True if the user has all permissions without explicitly assigning them.

{{ user.is_superuser }}

last_login

The date and time of the user’s last login.

{{ user.last_login }}

date_joined

The date and time when the user account was created.

{{ user.date_joined }}

groups

A related manager for handling the groups the user belongs to.

{{ user.groups.all }}

user_permissions

A related manager for handling the permissions the user has.

{{ user.user_permissions.all }}

14.4.3.2. Methods (Functions)#

Method

Purpose

Example Usage

get_username()

Returns the username for the user.

{{ user.get_username }}

get_full_name()

Returns the first name and the last name with a space in between.

{{ user.get_full_name }}

get_short_name()

Returns the first name of the user.

{{ user.get_short_name }}

set_password(raw_password)

Sets the user’s password to the given raw string, taking care of the password hashing.

user.set_password('new_password')

check_password(raw_password)

Returns True if the given raw string is the correct password for the user.

user.check_password('password')

set_unusable_password()

Marks the user as having no password set.

user.set_unusable_password()

has_usable_password()

Returns True if the user has a usable password.

user.has_usable_password()

email_user(subject, message, from_email=None, **kwargs)

Sends an email to the user.

user.email_user('Subject', 'Message')

get_group_permissions(obj=None)

Returns a list of permission strings that this user has through their groups.

user.get_group_permissions()

get_all_permissions(obj=None)

Returns a list of permission strings that this user has, both through group and user permissions.

user.get_all_permissions()

has_perm(perm, obj=None)

Returns True if the user has the specified permission.

user.has_perm('app_label.permission_code')

has_perms(perms, obj=None)

Returns True if the user has each of the specified permissions.

user.has_perms(['app_label.perm1', 'perm2'])

has_module_perms(app_label)

Returns True if the user has any permissions in the given app label.

user.has_module_perms('app_label')

is_authenticated (property)

Always True for any user. This is a way to tell if the user has been authenticated.

{% if user.is_authenticated %}...{% endif %}

is_anonymous (property)

Always False for any user. This is a way to tell if the user is not authenticated.

{% if user.is_anonymous %}...{% endif %}

14.4.4. Step 4: Configure Settings#

  • Set the login redirect URL and the login URL in your django_project/setting.py

  • LOGIN_URL: This setting defines the URL where users will be redirected if they need to log in. For instance, if a user tries to access a page that requires authentication without being logged in, they will be redirected to this URL.

  • LOGIN_REDIRECT_URL: This setting defines the URL where users will be redirected after successfully logging in.

    # settings.py
    LOGIN_REDIRECT_URL = 'profile'
    LOGIN_URL = 'login'
    

14.5. Profile and Picture (Customize forms)#

  • Install pillow for image processing

    pip install pillow
    

14.5.1. Step 1: Create UserProfile Model and configure Media settings#

  • The UserProfile model is an extension of the default Django User model.

  • It allows us to store additional information about the user, such as a bio, profile picture, and birth date.

    ## users/models.py
    from django.db import models
    from django.contrib.auth.models import User
    
    class UserProfile(models.Model):
        user = models.OneToOneField(User, on_delete=models.CASCADE)
        bio = models.TextField(default="This user does not have a bio", blank=True)
        image = models.ImageField(default='default.png', upload_to='profile_pics')
        birth_date = models.DateField(null=True, blank=True)
    
        def __str__(self):
            return f'{self.user.username} Profile'
    
    
  • Define where uploaded media files (like profile pictures) are stored and how they are accessed.

    # django_project/settings.py
    
    import os
    ...
    
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
    MEDIA_URL = '/media/'
    

14.5.2. Step 2: Construct the URL Pattern#

  • Define the URL pattern to access the user’s profile page.

    ## users/urls.py
    from django.urls import path
    from . import views
    
    urlpatterns = [
        ...,
        path('profile/', views.profile, name='profile'),
    ]
    
    

14.5.3. Step 3: Create Profile View#

  • The view handles the logic of rendering the user profile page.

  • It ensures that only authenticated users can access the profile page.

    ## users/views.py
    from django.contrib.auth.decorators import login_required
    from django.shortcuts import render
    
    ...
    
    @login_required
    def profile(request):
        return render(request, 'users/profile.html')
    
    

14.5.4. Step 4: Construct the Template#

  • It displays the user’s information and provides a form for updating the profile.

    <!-- templates/users/profile.html -->
    {% extends "blog/main.html" %}
    {% load crispy_forms_tags %}
    
    {% block main-content %}
    
    <h2>User Profile</h2>
    
    <div class="content-section">
        <div class="row">
            <div class="col-md-3">
                <img class="rounded-circle account-img" src="{{ user.userprofile.image.url }}" />
            </div>
            <div class="col-md-9">
                <h2 class="account-heading">{{ user.username }}</h2>
                <p class="text-secondary">{{ user.email }}</p>
                <ul class="list-group">
                    <li class="list-group-item">
                        <strong>Bio:</strong> {{ user.userprofile.bio }}
                    </li>
                    <li class="list-group-item">
                        <strong>Birth Date:</strong> {{ user.userprofile.birth_date|date:"F d, Y" }}
                    </li>
                </ul>
            </div>
        </div>
        <!-- FORM HERE -->
    </div>
    
    {% endblock main-content %}
    

14.5.5. Step 5: Create Signals#

  • Use Django signals to automatically create and save a UserProfile instance whenever a new User instance is created.

  • This ensures that every user has a profile.

    # users/signals.py
    
    from django.db.models.signals import post_save
    from django.contrib.auth.models import User
    from django.dispatch import receiver
    from .models import UserProfile
    
    @receiver(post_save, sender=User)
    def create_profile(sender, instance, created, **kwargs):
        if created:
            UserProfile.objects.create(user=instance)
    
    @receiver(post_save, sender=User)
    def save_profile(sender, instance, **kwargs):
        instance.userprofile.save()
    
    

14.5.6. Step 6: Construct Two Forms (User and UserProfile)#

  • Purpose: Create forms for updating user information and user profile information.

  • These forms will be used to handle the input from the profile page.

    # users/forms.py
    from django import forms
    from django.contrib.auth.models import User
    from .models import UserProfile
    from crispy_forms.helper import FormHelper
    from crispy_forms.layout import Submit
    
    ...
    
    class UserUpdateForm(forms.ModelForm):
        email = forms.EmailField()
        
        class Meta:
            model = User
            fields = ['username', 'email']
    
    class UserProfileForm(forms.ModelForm):
        class Meta:
            model = UserProfile
            fields = ['bio', 'image', 'birth_date']
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.helper = FormHelper()
            self.helper.add_input(Submit('update', 'Update'))
    
    

14.5.7. Step 7: Update the Profile View#

  • Modify the profile view to handle POST requests for form submission and update user and user profile data.

    # users/views.py
    
    from django.contrib import messages
    from django.shortcuts import redirect
    
    ...
    
    @login_required
    def profile(request):
        if request.method == 'POST':
            u_form = UserUpdateForm(request.POST, instance=request.user)
            p_form = UserProfileForm(request.POST, request.FILES, instance=request.user.userprofile)
            if u_form.is_valid() and p_form.is_valid():
                u_form.save()
                p_form.save()
                messages.success(request, f'Your account has been updated')
                return redirect('profile')
        else:
            u_form = UserUpdateForm(instance=request.user)
            p_form = UserProfileForm(instance=request.user.userprofile)
        
        context = {
            'u_form': u_form,
            'p_form': p_form
        }
        
        return render(request, 'users/profile.html', context=context)
    

14.5.8. Step 8: Update the Template#

  • Modify the template to include forms for updating the user and user profile information.

    <!-- users/templates/users/profile.html -->
    {% extends "blog/main.html" %}
    {% load crispy_forms_tags %}
    
    {% block main-content %}
    
    <h2>{{ user.username }}'s Profile</h2>
    
    <div class="content-section">
        <div class="media">
            <img class="rounded-circle account-img" src="{{ user.userprofile.image.url }}" />
            <div class="media-body">
                <h2 class="account-heading">{{ user.username }}</h2>
                <p class="text-secondary">{{ user.email }}</p>
                <ul class="list-group">
                    <li class="list-group-item">
                        <strong>Bio:</strong> {{ user.userprofile.bio }}
                    </li>
                    <li class="list-group-item">
                        <strong>Birth Date:</strong> {{ user.userprofile.birth_date|date:"F d, Y" }}
                    </li>
                </ul>
            </div>
        </div>
        <form method="POST" enctype="multipart/form-data">
            {% csrf_token %}
            <fieldset class="form-group">
                <legend class="border-bottom mb-4">Update Profile</legend>
                {{ u_form|crispy }}
                {{ p_form|crispy }}
            </fieldset>
            <div class="form-group">
                <button class="btn btn-outline-info" type="submit">Update</button>
            </div>
        </form>
    </div>
    
    {% endblock main-content %}
    

14.5.9. Step 9: Modify the UserProfile Model to Resize Images#

  • Update the UserProfile model to resize profile images to a maximum of 300x300 pixels to ensure uniformity and optimize storage space.

    # users/models.py
    
    from django.db import models
    from django.contrib.auth.models import User
    from PIL import Image
    
    class UserProfile(models.Model):
        user = models.OneToOneField(User, on_delete=models.CASCADE)
        bio = models.TextField(default="This user does not have a bio", blank=True)
        image = models.ImageField(default='default.png', upload_to='profile_pics')
        birth_date = models.DateField(null=True, blank=True)
    
        def __str__(self):
            return f'{self.user.username} Profile'
        
        def save(self, *args, **kwargs):
            super().save(*args, **kwargs)
            
            # Resize uploaded image
            img = Image.open(self.image.path)
            if img.height > 300 or img.width > 300:
                output_size = (300, 300)
                img.thumbnail(output_size)
                img.save(self.image.path)
    
    

14.6. Additional Resource#

14.6.1. Commonly Used Decorators#

Decorator

Purpose

Use Example

@login_required

Ensures that the user is authenticated before accessing the view.

@login_required
def profile(request):
    return render(request, 'profile.html')

@permission_required

Ensures that the user has a specific permission before accessing the view.

@permission_required('app_label.permission_code')
def my_view(request):
    ...

@user_passes_test

Allows access to the view if a given test is passed (e.g., custom check).

@user_passes_test(lambda u: u.is_superuser)
def my_view(request):
    ...

@csrf_exempt

Exempts the view from CSRF verification.

@csrf_exempt
def my_view(request):
    ...

@require_http_methods

Restricts the allowed HTTP methods for the view.

@require_http_methods(["GET", "POST"])
def my_view(request):
    ...

@require_GET

Restricts the view to handle only GET requests.

@require_GET
def my_view(request):
    ...

@require_POST

Restricts the view to handle only POST requests.

@require_POST
def my_view(request):
    ...

@cache_page

Caches the view’s response for a specified amount of time.

@cache_page(60 * 15)
def my_view(request):
    ...

@never_cache

Prevents caching of the view’s response.

@never_cache
def my_view(request):
    ...

@vary_on_headers

Varies the cache based on specific headers.

@vary_on_headers('User-Agent')
def my_view(request):
    ...

@method_decorator

Applies a decorator to a class-based view method.

@method_decorator(login_required, name='dispatch')
class MyView(View):
    ...

14.6.2. Commonly used Signals#

Signal Purpose Example
post_save Sent after a model’s save() method is called. Automatically create a profile for new users:
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import UserProfile

@receiver(post_save, sender=User) def create_profile(sender, instance, created, **kwargs): if created: UserProfile.objects.create(user=instance)

pre_save Sent before a model’s save() method is called. Auto-populate a field before saving:
from django.db.models.signals import pre_save
from django.dispatch import receiver
from .models import MyModel

@receiver(pre_save, sender=MyModel) def pre_save_handler(sender, instance, **kwargs): instance.slug = slugify(instance.name)

post_delete Sent after a model’s delete() method is called. Clean up related records after deletion:
from django.db.models.signals import post_delete
from django.dispatch import receiver
from .models import MyModel

@receiver(post_delete, sender=MyModel) def post_delete_handler(sender, instance, **kwargs): instance.related_record.delete()

pre_delete Sent before a model’s delete() method is called. Perform actions before deletion:
from django.db.models.signals import pre_delete
from django.dispatch import receiver
from .models import MyModel

@receiver(pre_delete, sender=MyModel) def pre_delete_handler(sender, instance, **kwargs): print(f’Deleting: {instance}’)

m2m_changed Sent when a ManyToManyField is changed. Log changes to many-to-many relationships:
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
from .models import MyModel

@receiver(m2m_changed, sender=MyModel.many_to_many_field.through) def m2m_changed_handler(sender, instance, action, **kwargs): if action == “post_add”: print(f’Added to {instance}’)

request_started Sent when Django starts processing an HTTP request. Initialize per-request data:
from django.core.signals import request_started
from django.dispatch import receiver

@receiver(request_started) def request_started_handler(sender, **kwargs): print(‘Request started’)

request_finished Sent when Django finishes processing an HTTP request. Clean up after request processing:
from django.core.signals import request_finished
from django.dispatch import receiver

@receiver(request_finished) def request_finished_handler(sender, **kwargs): print(‘Request finished’)

user_logged_in Sent when a user logs in. Track user login activities:
from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver

@receiver(user_logged_in) def user_logged_in_handler(sender, request, user, **kwargs): print(f’User {user} logged in’)

user_logged_out Sent when a user logs out. Track user logout activities:
from django.contrib.auth.signals import user_logged_out
from django.dispatch import receiver

@receiver(user_logged_out) def user_logged_out_handler(sender, request, user, **kwargs): print(f’User {user} logged out’)

user_signup Custom signal, often used to handle user sign-up events. Handle additional sign-up logic:
from django.dispatch import Signal

user_signup = Signal(providing_args=[“user”, “request”])

@receiver(user_signup) def user_signup_handler(sender, user, request, **kwargs): print(f’User {user} signed up’)