Introduction
An authentication system is a critical component of any modern application. In other to ensure secure access, protect sensitive data, and provide personalized user experiences, then implementing a robust authentication mechanism is essential.
In this article, I will guide you through building a Django Login and Registration system using a custom user model. Whether you're starting from scratch or enhancing an existing application, this tutorial will provide you with the insights needed to build a fully functional Django authentication system ready to integrate seamlessly into your next project.
Check out a live demo of the app, which showcases functionality similar to what you'll be building
Download the complete code from the GitHub repository
Let’s get started!
Table of Contents
- Understanding Authentication and Authorization
- Setting Up Your Environment
- Creating an Index Page
- Integrating the View into Django's MVC Architecture.
- Creating Custom User Model
- Create Registration Form
- Create Views for Registration, Login, and Logout
- Create URLs for the Views
- Testing User Registration
- What's Next
- Conclusion
Prerequisites
Before proceeding, ensure you:
- Have basic knowledge of the Django framework.
- Understand fundamentals of HTML and Bootstrap. If you need a refresher on HTML and Bootstrap, you can explore resources on w3schools.com.
Let's proceed
Understanding Authentication and Authorization
When building secure web applications or services, two key concepts often arise: authentication and authorization. Although these terms are sometimes used interchangeably, they refer to distinct processes with different roles.
Authentication is the act of confirming the identity of a user or system. It answers the question, “Who are you?” This process ensures that the entity attempting to gain access is legitimate. Common methods include username-password combinations, biometric scans, and multi-factor authentication (MFA)
Authorization, in contrast, determines what an authenticated user or system is allowed to do. It answers the question, “What can you access?” Authorization defines access levels, dictating which resources or actions are available to a particular user based on roles, permissions, or policies. For example, an authenticated user may log into a system, but their authorization level will determine whether they can view, edit, or delete certain data.
NOTE: While Django provides several authentication methods, including token-based and social authentication. For this guide, we will use Django's built-in authentication system, which primarily relies on session-based authentication.
Setting Up Your Environment
Before getting started, ensure you have Python and pip installed on your system. If Python is not installed, you can download and install it from the official Python website.
Create and Activate a Virtual Environment
It's best practice to use a virtual environment for your project to manage dependencies efficiently. Open your terminal in the desired location and run:
Python -m venv djangoLoginRegistration_env
&& cd djangoLoginRegistration_env/bin/activate.
The command creates a virtual environment named djangoLoginRegistration_env and activates it.
This command is specific to Windows OS. If you're using a different operating system, search for instructions on how to create and activate a virtual environment for your OS. Once the virtual environment is set up, the remaining steps will be the same.
Install Django
Once the virtual environment is activated, install Django by running:
pip install Django
Once you have Django installed, create a new project.
django-admin startproject djangoCustomLoginAndRegistrationSystem
cd djangoCustomLoginAndRegistrationSystem
I named the project djangoCustomLoginAndRegistrationSystem. Admittedly, it’s a long name, so feel free to choose a different name for your project.
Next, create a new Django app that will handle the authentication logic. I’ll name it userauth
python manage.py startapp userauth
Update settings.py
In your project settings, add userauth to the INSTALLED_APPS list to include the authentication app.
# django_auth_project/settings.py
INSTALLED_APPS = [
…
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
'userauth.apps.UserauthConfig', # Add this line for the new auth app
]
Start Your Server
Start the development server by running:
python manage.py runserver
Navigate to http://127.0.0.1:8000/. At this stage, you’ll see Django’s default homepage, which is quite basic. In the next steps, we’ll create a more structured and visually appealing homepage.
Note: After starting the server, you might notice warnings about unapplied migrations. For now, you can ignore them—we’ll address migrations later when we extend the Django User model to include additional profile_picture and email fields.
You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
January 18, 2025 - 07:38:10
Django version 4.2.13, using settings 'djangoCustomLoginAndRegistrationSystem.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
Creating an Index Page
Let's create an index page for our application, which will also serve as a test endpoint for redirection after logout.
Understanding Django Template files
Django provides two options for managing template files-at the project level and the app level.
We’ll use the app-level approach since it allows for better organization, especially when working with multiple apps—which is common in authentication systems. This approach also makes future expansion easier.
Setting Up the Template Structure
Inside the userauth app folder, create a new folder named templates. Within the templates folder, create another folder named userauth.
If you’re new to Django, this folder structure follows Django’s best practices for managing templates within individual apps.
Once the folders are set up, create a file named index.html
and add the following content:
<!-- /userauth/templates/userauth/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Django Auth Application</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<style>
.bg-custom-grey {
background-color: #eeeeee; /* Light grey color */
}
/* Custom teal color for the heading */
.text-teal {
color: #008080; /* Teal color */
}
</style>
</head>
<body>
<!-- Navbar Section -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-0">
<div class="container">
<a class="navbar-brand" href="#">Auth App</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="#">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Register</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Hero Section with Grey Background -->
<section class="bg-custom-grey text-center py-5" style="height:370px;">
<div class="container" style="padding-top: 40px;">
<h1 class="display-4 text-teal">Django Authentication System</h1>
<p class="lead"><em>A Bespoke user authentication system using Django's built-in authentication tools.</em></p>
<a href="#" class="btn btn-light btn-lg">Learn More</a>
</div>
</section>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
The code above defines a simple web page that utilizes Bootstrap 5 for styling and layout. It features a navigation bar and a hero section with a custom grey background
The highlights here are:
-
<a class="nav-link" href="#">Login</a>
: A link to a login page. -
<a class="nav-link" href="#">Register</a>
: A link to a registration page.
Integrating the View into Django's MVC Architecture.
Django follows the MVC (Model-View-Controller) architecture, though it is commonly referred to as MVT (Model-View-Template) in Django terminology.
At this stage, we have created the necessary index template file, but we still need to define the view and set up the corresponding URL configuration (which acts as the controller) to complete this setup. This will allow us to render the page properly in the browser.
A model is not required at this point since we are not interacting with a database yet. However, as we progress and define a custom user model, we will fully implement the MVC/MVT architecture, integrating models for data management and persistence.
Let's start with the View Component
Define the View
Navigate to views.py
inside the userauth app folder and add the following code:
# djangoCustomLoginAndRegistrationSystem/userauth/views.py
from django.shortcuts import render
# Homepage view
def index(request):
return render(request, 'userauth/index.html')
Map the View to a URL
To make the view accessible, we need to define a URL route for it.
Create an urls.py
file inside the userauth app folder (if it doesn’t already exist) and add the following code:
#djangoCustomLoginAndRegistrationSystem/userauth/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'), # Homepage route
]
Register App URLs in the Project-Level urls.py
Next, we need to include the app-level urls.py
in the project-level urls.py
file so that Django recognizes our app's routes.
Open djangoCustomLoginAndRegistrationSystem/urls.py
and update it as follows:
#djangoCustomLoginAndRegistrationSystem/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path('', include('userauth.urls')), # Include `userauth` app URLs
]
Start the Server
After completing all the steps, start the Django development server:
python manage.py runserver
Now, visit http://127.0.0.1:8000/ in your browser. You should see the homepage successfully displayed.
Great!.
Now that our homepage is set up, let’s integrate the Model component.
Creating Custom User Model
At the core of Django's built-in authentication system is the User model. This model stores essential user information such as: username, first_name, last_name, date_joined, and more.
While Django’s default authentication system is fully functional, there are cases where you might need additional fields—such as phone numbers, or profile pictures.
To handle this, Django provides the AbstractUser class, which allows us to extend the default User Model and introduce custom fields without modifying Django’s built-in structure.
Since our system will include a profile picture and email, we’ll extend the AbstractUser model to include these fields.
The Custom User Model
Open the models.py
file inside your userauth app.
Paste the following code:
# /userauth/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
profile_pic = models.ImageField(upload_to='profile_pics/', blank=True, null=True)
email = models.EmailField(unique=True) # Enforce unique emails
# Fix reverse accessor conflicts with unique related names
groups = models.ManyToManyField(
'auth.Group',
related_name='custom_users',
blank=True,
help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.',
verbose_name='groups',
)
user_permissions = models.ManyToManyField(
'auth.Permission',
related_name='custom_users_permissions',
blank=True,
help_text='Specific permissions for this user.',
verbose_name='user permissions',
)
Highlights:
The code above sets up a custom user model by extending Django’s AbstractUser class. This approach inherits all the standard functionality of AbstractUser
while enhancing it with additional fields, such as profile_pic
and email
.
- The
profile_pic
field stores the user's profile picture, usingmodels.ImageField
to handle image uploads. Theupload_to='profile_pics/'
argument specifies the directory where uploaded images will be saved. - Setting
blank=True
andnull=True
ensures these fields can remain empty if needed.
Additionally, including the groups
and user_permissions
fields in a custom user model allows for integration with Django’s built-in permission and group management systems.
Note: If you want to create an entirely new user model from scratch without retaining the default fields of Django’s user model, you should use the AbstractBaseUser class instead of
AbstractUser
. This provides complete control over the user model’s structure and behavior.
With this in place, we now have a custom user model that supports profile picture and email field. But before then, we have to configure Django to recognize and use this model as the default authentication model.
So, open settings.py
and add this line:
AUTH_USER_MODEL = ' userauth.CustomUser'
Now, it’s time to apply migrations.
For this demo, we’ll use SQLite, Django’s default database. However, you can easily switch to more robust databases like PostgreSQL or MySQL based on your project requirements.
To create the database, run the migrations by executing the following command:
python manage.py makemigrations userauth
python manage.py migrate
This will set up the necessary database tables and prepare your application for use.
OUTPUT:
Migrations for 'userauth':
userauth\migrations\0001_initial.py
- Create model CustomUser
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, userauth
Running migrations:
Applying contenttypes.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0001_initial... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying userauth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying sessions.0001_initial... OK
Next, we’ll need to create forms for user registration and login in userauth/forms.py
.
Create Registration Form
Django's authentication system comes with built-in features like LoginView
and LogoutView
, along with their corresponding routes and base forms such as the AuthenticationForm
. This setup simplifies the login and logout processes, requiring only the creation of template views.
However, Django does not provide a built-in solution for registration or sign-up forms. This means you’ll need to create a custom registration form to handle user sign-ups.
To achieve this, we'll create a CustomUserCreationForm
by subclassing Django's generic UserCreationForm
.
The
UserCreationForm
is designed to handle user creation but is tied to Django's default User model. By customizing it, we can adapt it to work with our custom user model- in this case, using an email address as the primary identifier.
So, inside your userauth
app, create a new file named forms.py
and add the following code:
# /userauth/forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
email = forms.EmailField(required=True, max_length=254) # Make email required
class Meta:
model = CustomUser
fields = ['username', 'email', 'password1', 'password2']
widgets = {
'username': forms.TextInput(attrs={'class': 'form-control'}),
'email': forms.EmailInput(attrs={'class': 'form-control'}),
'password1': forms.PasswordInput(attrs={'class': 'form-control'}),
'password2': forms.PasswordInput(attrs={'class': 'form-control'}),
}
Note: In the form, I included both
username
and
Next Steps?
Now that we’ve created our registration form, the next steps are to:
- Set up authentication views to handle registration, login, and logout.
- Create corresponding HTML templates for user input and interaction.
Let’s move on to implementing the views!
Create Views for Registration, Login, and Logout
Before diving into the code, let’s quickly understand the workflow:
Registration Workflow:
When a user accesses the registration page, the
RegisterView
class handles the process. It renders theCustomUserCreationForm
using theregister.html
template (which we’ll create later).When the user submits the form, the system validates the input fields. If the form is valid, the user’s credentials are saved, and the user is automatically logged in and redirected to their profile page (
profile.html
), where a welcome message displays their username.
Profile Access Workflow:
Accessing the profile page requires authentication.
If a logged-in user accesses the profile page, the
profile.html
template is rendered.If an unauthenticated user tries to access the profile page, they are redirected to the login page (
login.html
).
Login and Logout Workflow:
The
CustomLoginView
handles user login, rendering thelogin.html
template.Upon successful login, the user is redirected to their profile page.
The
LogoutView
handles user logout, redirecting the user to the homepage (index.html
) after logout.
Implementing the Logic
Open your views.py
file and add the following code:
# userauth/views.py
from django.shortcuts import render, redirect
from django.urls import reverse_lazy
from django.views.generic import TemplateView, CreateView
from django.contrib.auth import login
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
from .forms import CustomUserCreationForm
from django.contrib.auth.views import LoginView
# Homepage view
def index(request):
return render(request, 'userauth/index.html')
class RegisterView(SuccessMessageMixin, CreateView):
template_name = 'userauth/register.html'
form_class = CustomUserCreationForm
success_url = reverse_lazy('profile')
success_message = "Registration successful. Welcome!"
def form_valid(self, form):
# Save the user and log them in
user = form.save()
login(self.request, user)
return super().form_valid(form)
class CustomLoginView(LoginView):
template_name = 'userauth/login.html'
class ProfileView(LoginRequiredMixin, TemplateView):
template_name = 'userauth/profile.html'
Explanation of the Code:
The above code uses CustomUserCreationForm
to handle user registration, with a logic designed to automatically logs in the user after successful registration and redirects them to the profile page. For login access, CustomLoginView
is called which renders the login.html
template and subsequently redirect authenticated users to their profile page. The ProfileView
requires authentication (using LoginRequiredMixin
); renders the profile.html
template and passes the logged-in user’s username to the context.(check auhenticity). Lastly, the CustomLogoutView
handles user logout and redirects to the homepage (index.html
).
Note: Django offers two types of views, - The function-based view and class-based view. I have used the class-based form here by subclassing the Django generic view
CreateView
class for theRegisterView
andLoginView
forCustomLoginView
class
Now that we've completed the views, let's map their corresponding URLs to them.
Create URLs for the Views
Here, we'll define URLs for registration, login, and logout and profile views.
Update userauth/urls.py
with the following code
# userauth/urls.py
from django.urls import path
from django.contrib.auth.views import LogoutView
from .views import RegisterView, ProfileView, CustomLoginView
from . import views
urlpatterns = [
path('', views.index, name='index'), # Home page view
path('register/', RegisterView.as_view(), name='register'),
path('profile/', ProfileView.as_view(), name='profile'),
path('login/', CustomLoginView.as_view(), name='login'),
path('logout/', LogoutView.as_view(next_page='index'), name='logout'), # Redirect to index page after logout
]
Great! Now, let's integrate the final piece of the puzzle—creating template files—to complete our user authentication system. We'll start with the registration template.
Creating the Registration Template
Navigate to the userauth/templates/userauth/
directory and create a new file named register.html
. Once created, add the following code inside it:
<!-- /userauth/templates/userauth/register.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Registration</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
{% if messages %}
<div class="messages">
<!--<div class="container mt-3">-->
{% for message in messages %}
<div class="alert alert-{{ message.tags|default:'info' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
</div>
{% endif %}
<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-0">
<div class="container">
<a class="navbar-brand" href="#">Auth App</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="#">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'register' %}">Register</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container-fluid vh-100 d-flex">
<div class="col-lg-6 d-flex flex-column justify-content-center align-items-center">
<h2 class="mb-4">Create an account</h2>
<form id="register-form" class="w-75" method="post" action="{% url 'register' %}">
{% csrf_token %}
{{ form.as_p }}
<div class="d-grid">
<button type="submit" class="btn btn-dark">Register</button>
</div>
</form>
<p class="mt-3">Already have an account? <a href="{% url 'login' %}" class="text-dark fw-bold text-decoration-none">Log in</a></p>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
Now, start the development server:
python manage.py runserver
Then, navigate to http://localhost:8000/register in your browser.
At this point, the registration page is displayed. However, the design is quite plain, and Django’s default warning messages don’t align with our intended UI. To achieve a better user experience, we will replace the existing register.html template with a more visually appealing one.
Replace register.html
Replace current register.html
with the following code:
<!-- /userauth/templates/userauth/register.html -->
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container-fluid vh-100 d-flex">
<!-- Left Section -->
<div class="col-lg-6 bg-dark text-white d-flex flex-column justify-content-center align-items-center">
<h1 class="mb-3">Pi-Health Hub</h1>
<p class="mb-5">Create your account to get started</p>
<img src="{% static 'assets/mri.png' %}" alt="Checkout Illustration" class="img-fluid" style="max-width: 400px;">
</div>
<!-- Right Section -->
<div class="col-lg-6 d-flex flex-column justify-content-center align-items-center">
<h2 class="mb-4">Create an account</h2>
<form id="register-form" class="w-75" method="post">
{% csrf_token %}
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" id="username" name="username" class="form-control" placeholder="Choose a username" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email Address</label>
<input type="email" id="email" name="email" class="form-control" placeholder="Enter your email" required>
</div>
<div class="mb-3">
<label for="password1" class="form-label">Password</label>
<input type="password" id="password1" name="password1" class="form-control" placeholder="Enter a password" required>
</div>
<div class="mb-3">
<label for="password2" class="form-label">Confirm Password</label>
<input type="password" id="password2" name="password2" class="form-control" placeholder="Confirm your password" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-dark">Register</button>
</div>
</form>
<p class="mt-3">Already have an account? <a href="{% url 'login' %}" class="text-dark fw-bold text-decoration-none">Log in</a></p>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
Create login.html
Create a new file named login.html
and add the following code:
<!-- /userauth/templates/userauth/login.html -->
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container-fluid vh-100 d-flex">
<!-- Left Section -->
<div class="col-lg-6 bg-dark text-white d-flex flex-column justify-content-center align-items-center">
<h1 class="mb-3">Pi-Health Hub</h1>
<p class="mb-5">Login to access your account</p>
<img src="{% static 'assets/mri.png' %}" alt="Checkout Illustration" class="img-fluid" style="max-width: 400px;">
</div>
<!-- Right Section -->
<div class="col-lg-6 d-flex flex-column justify-content-center align-items-center">
<h2 class="mb-4">Login to your account</h2>
<form id="login-form" method="post" action="{% url 'login' %}" class="w-75">
{% csrf_token %}
<div class="mb-3">
<label for="id_username" class="form-label">Username</label>
<input type="text" name="username" id="id_username" class="form-control" placeholder="Enter your username" required>
</div>
<div class="mb-3 position-relative">
<label for="id_password" class="form-label">Password</label>
<input type="password" name="password" id="id_password" class="form-control" placeholder="Enter your password" required>
<br>
<a href="#" class="small position-absolute end-0 mt-1 text-decoration-none">Reset Password!</a>
<br>
</div>
{% if form.errors %}
<div class="alert alert-danger">
{% for field in form %}
{% for error in field.errors %}
<p>{{ error }}</p>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<p>{{ error }}</p>
{% endfor %}
</div>
{% endif %}
<div class="d-grid">
<button type="submit" class="btn btn-dark">Log in</button>
</div>
</form>
<p class="mt-3">New user? <a href="{% url 'register' %}" class="text-dark fw-bold text-decoration-none">Create an account</a></p>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
Create profile.html
Also, create a new file named profile.html
and add the following code:
<!-- /userauth/templates/userauth/profile.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Profile</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-0">
<div class="container">
<a class="navbar-brand" href="#">Auth App</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
{% if user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{% url 'logout' %}">Logout</a>
</li>
<!-- Display Profile Pic or Default Image if not available -->
<li class="nav-item">
<img
src="{% if user.profile_pic %}{{ user.profile_pic.url }}{% else %}/static/assets/male-avatar.jpeg{% endif %}"
alt="Profile Picture"
class="profile-pic ms-2"
style="width: 40px; height: 40px; object-fit: cover; border-radius: 50%;">
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="container vh-100 d-flex" style="margin-top: 30px;">
Welcome, {{ user.username }}!
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
Also, we need to modify index.html
template
Update index.html
Replace the content of index.html
to conditionally display content based on the user's authentication status:
<!-- /userauth/templates/userauth/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Django Auth Application</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<style>
.bg-custom-grey {
background-color: #eeeeee; /* Light grey color */
}
/* Custom teal color for the heading */
.text-teal {
color: #008080; /* Teal color */
}
</style>
</head>
<body>
<!-- Navbar Section -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-0">
<div class="container">
<a class="navbar-brand" href="#">Auth App</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
{% if user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{% url 'logout' %}">Logout</a>
</li>
<!-- Display Profile Pic or Default Image if not available -->
<li class="nav-item">
<img
src="{% if user.profile_pic %}{{ user.profile_pic.url }}{% else %}/static/male-avatar.jpeg{% endif %}"
alt="Profile Picture"
class="profile-pic ms-2"
style="width: 40px; height: 40px; object-fit: cover; border-radius: 50%;">
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'login' %}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'register' %}">Register</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<!-- Hero Section with Grey Background -->
<!-- Hero Section with Increased Height -->
<section class="bg-custom-grey text-center py-5" style="min-height: 350px; display: flex; align-items: center;">
<div class="container" style="margin-top: -40px;">
<h2 class="display-4 text-teal">Django Authentication System</h2>
<p class="lead"><em>A Bespoke user authentication system using Django's built-in authentication tools.</em></p>
<a href="#" class="btn btn-light btn-lg" style="margin-top: 25px;">Learn More</a>
</div>
</section>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
Code Explanation:
The navigation bar in index.html
dynamically adapts based on whether the user is authenticated or not:
- If the user is authenticated, the navbar displays a logout option and the user’s profile picture (a default avatar is used if no picture is uploaded).
- If the user is not logged in, it provides options to log in or register.
- This dynamic rendering is made possible using Django’s template tags:
{% if user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{% url 'logout' %}">Logout</a>
</li>
<!-- Display Profile Pic or Default Image if not available -->
<li class="nav-item">
<img
src="{% if user.profile_pic %}{{ user.profile_pic.url }}{% else %}/static/male-avatar.jpeg{% endif %}"
alt="Profile Picture"
class="profile-pic ms-2"
style="width: 40px; height: 40px; object-fit: cover; border-radius: 50%;">
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'login' %}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'register' %}">Register</a>
</li>
{% endif %}
One more thing—if you’ve noticed, I have imported {% load static %}
at the top of the HTML files. This is necessary because we need to render static files, such as the profile picture avatar and the authentication interface image.
Steps to Set Up Static Files:
- Download the image files from here.
- Create a static folder inside the userauth folder.
- Inside the static folder, create another folder named assets.
- Extract the downloaded images into the assets folder.
That’s it! The images will now be displayed correctly in the specified locations in our HTML files.
One last thing, we need to specify login redirects in settings.py
Update Django Settings
To ensure users are redirected to their profile page after logging in, add the following line to settings.py
:
# settings.py
LOGIN_REDIRECT_URL = '/profile/' # Redirect to profile after login
This tells Django to redirect authenticated users to the profile page after logging in.
Save and Rerun the Server
Save all the changes and restart the development server:
python manage.py runserver
Now, you should have beautifully styled registration (http://localhost:8000/register) and login (http://localhost:8000/login) pages.
Testing User Registration
Navigate to the registration page (http://localhost:8000/register).
Fill in the required credentials (username, email, password, etc.). Ensure your username and password are unique and not too similar. Once done, click the Register button to create the user.
Oops! Encountering a 404 Error
After registering, you may encounter the following error:
Page not found (404)
Request Method: GET
Request URL: http://localhost:8000/accounts/login/?next=/profile/
Understanding and Fixing the Error
Don’t panic! This error occurs because Django’s authentication system defaults to using /accounts/login/ for login redirection, which doesn’t exist in our setup.
To fix this, we need to explicitly set the login URL in settings.py:
#settings.py
LOGIN_URL = '/login/'
Save the changes and restart the server again:
python manage.py runserver
Now, everything should work smoothly!
Go ahead to test the registration, login, and profile access workflows again.
Register, and once successful, you’ll be redirect to your profile page with a welcome message.
What's Next
Congrats on making it this far! We’ve successfully built our custom authentication system, but there’s always room for improvement. Here are a few areas where you can enhance the system further:
- Refactoring Repetitive HTML Code: Some HTML structures are repeated across multiple pages. Instead of duplicating code, you can leverage Django’s template inheritance to maintain a cleaner, more efficient codebase. Create a base template that includes common elements (e.g., navbar, footer), and let other pages extend from it.
- Profile Picture Uploads: Currently, users are assigned a default profile picture, but they cannot update their photo. You can allow users to upload and manage their profile images through the authentication system.
- Enhancing Account Management: You can implement additional features such as password reset, email verification, and two-factor authentication (2FA) to improve security and user experience.
- User Permissions & Access Control: Once a user is authenticated, what actions should they be allowed to perform? You can implement role-based access control (RBAC) to define different levels of permissions for users (e.g., admin, editor, viewer).
You can download the full code from the GitHub repo
If you need further customizations or guidance, feel free to reach out. Happy coding!
Love the guide?
Your support will be appreciated
Conclusion
In this guide, we've built a custom Django Login and Registration system, covering everything from setting up the project to implementing user authentication and profile functionality.
Top comments (0)