We can build an authentication for our projects using the Django authentication system.
Prerequisites:
- Python
- OOP
- Django Basic knowledge(Django templates, views, URLs, settings)
This is what the Django documentation says about its authentication system:
Djangos authentication system in its default configuration. This configuration has evolved to serve the most common project needs, handling a reasonably wide range of tasks, and has a careful implementation of passwords and permissions. For projects where authentication needs differ from the default, Django supports extensive extension and customization of authentication.
Django authentication provides both authentication and authorization and is generally referred to as the authentication system, as these features are somewhat coupled.
We will use authentication views and built-in forms to create this app. The operations that this app will do are:
- User Registration
- Login
- Logout
- Password change
Setup
Let's start creating a folder for our project, then we create a virtual environment, and activate it:
mkdir django_authentication
cd django auth
py -m venv venv
cd venv/Scripts
activate
Now we install Django:
pip install django
We create a django project:
django_admin startproject django_authentication
cd django_authentication
Now we create our app:
py manage.py startapp user_auth
We have to add our app to django_authentication/settings.py
and the URLs our app will redirect the users when they login or logout :
settings.py
LOGIN_REDIRECT_URL = '/home'
LOGOUT_REDIRECT_URL = '/home'
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'user_auth',
]
After we add our app to INSTALLED_APPS
, then we run migrations using the following command in our terminal:
py manage.py migrate
According to the doc:
When django.contrib.auth is listed in your INSTALLED_APPS setting, it will ensure that four default permissions add, change, delete, and view are created for each Django model defined in one of your installed applications.
These permissions will be created when you run manage.py migrate; the first time you run migrate after adding django.contrib.auth to INSTALLED_APPS, the default permissions will be created for all previously-installed models, as well as for any new models being installed at that time. Afterward, it will create default permissions for new models each time you run manage.py migrate (the function that creates permissions is connected to the post_migrate signal).
We are going to implement Authentication Views to handle login, logout, and password management, we need to add django.contrib.auth.urls
to django_authentication/urls.py
django_authentication/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('users', include('django.contrib.auth.urls')),
]
This will include the following URL patterns:
users/login/ [name='login']
users/logout/ [name='logout']
users/password_change/ [name='password_change']
users/password_change/done/ [name='password_change_done']
users/password_reset/ [name='password_reset']
users/password_reset/done/ [name='password_reset_done']
users/reset/<uidb64>/<token>/ [name='password_reset_confirm']
users/reset/done/ [name='password_reset_complete']
We run the following command to run the server:
py manage.py runserver
In the user_auth
directory, we create a folder to store our app's templates.
user_auth/
__init__.py
admin.py
apps.py
migrations/
models.py
templates/
user_auth/
home.html
base.html
tests.py
urls.py
views.py
Home
views.py
from django.shortcuts import render
# Create your views here.
def home_page(request):
return render (request,"user_auth/home.html")
Here we just write a function that will render home.html
We have to create an urls.py
file inside our app's directory to add the URL of the views we will create.
user_auth/urls.py
from django.contrib import admin
from django.urls import path, include
from . import views
urlpatterns = [
path('', views.home_page, name="home"),
]
Now we have to include user_auth app URLs inside the project's urls.py
file.
django_authentication/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('users', include('django.contrib.auth.urls')),
path('home/', include('user_auth.urls')),
]
We create two templates. base.html
where we are going to add the HTML and CSS code that will be inherited by other HTML files. And home.html
that which will be the template of the home page.
base.html
<!doctype html>
<html lang="en">
<div class="topnav">
<a href="{% url 'home' %}">Home</a>
{% if user.is_authenticated %}
<a href="{% url 'logout' %}">Logout</a>
{% else %}
<a href="{% url 'login' %}">Login</a>
{% endif %}
<a href="{% url 'signup' %}">SingUp</a>
</div>
<div class="container">
{% block content %}
{% endblock content %}
</div>
</body>
<html>
<style type="text/css">
.topnav {
background-color: #333;
overflow: hidden;
position: fixed;
top: 0;
width: 100%;
}
.topnav a {
float: left;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
font-size: 17px;
font-family: sans-serif;
}
/* Change the color of links on hover */
.topnav a:hover {
background-color: #ddd;
color: black;
}
.topnav a.active {
background-color: #04AA6D;
color: white;
}
body {
background-color: #DEB887;
}
</style>
home.html
{% extends 'user_auth/base.html' %}
{% block content %}
<body>
<div class= "greeting">
<h1>Hello</h1>
<p> Welcome {{ user.username|default:'' }}, to the Our site</p>
</div>
</body>
<style>
.greeting {
color: #F0FFFF;
text-align: center;
font-family: sans-serif;
}
</style>
{% endblock content %}
Inside templates/user_auth, we create a folder named "registration". We create in it a template for login, signup, and a form_base template. In the form_base template, we add the HTML and CSS that will be inherited by the login and signup templates.
form_base.html
The HTML and CSS were copied from this tutorial of Dennis Ivy.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Django Auth</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300&display=swap" rel="stylesheet">
<style>
body {
background-color: #DEB887;
font-family: 'Poppins', sans-serif;
padding-top: 50px;
}
h1,
h2,
h3,
h4,
h5,
h6,
{
font-family:'Raleway', sans-serif;
}
a,
p {
color: #4b5156
}
.container{
max-width: 550px;
margin: auto;
background-color: #FFEBCD;
-webkit-box-shadow: 2px 2px 13px -4px rgba(0,0,0,0.21);
box-shadow: 2px 2px 13px -4px rgba(0,0,0,0.21);
}
input {
outline: none;
border: none;
}
.header-bar {
display:flex;
justify-content:space-between;
color:#fff;
padding:10px;
border-radius:5px 5px 0 0;
background: #5F9EA0;
}
.header-bar a {
color: rgb(247,247,247);
text-decoration:none;
}
input[type=text],
input[type=password],
textarea {
border: 1px solid #757575;
border-radius: 5px;
padding: 10px;
width: 90%;
}
label {
padding-top: 10px !important;
display: block;
}
::placeholder {
font-weight: 300;
opacity: 0.5;
}
.button {
border: 1px solid #757575;
background-color: #FFF;
color: #EB796F;
padding: 10px;
font-size: 14px;
border-radius: 5px;
cursor: pointer;
text-decoration: none;
}
.card-body {
padding: 20px;
}
.topnav {
background-color: #333;
overflow: hidden;
position: fixed; /* Set the navbar to fixed position */
top: 0; /* Position the navbar at the top of the page */
width: 100%; /* Full width */
}
/* Style the links inside the navigation bar */
.topnav a {
float: left;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
font-size: 17px;
font-family: sans-serif;
}
/* Change the color of links on hover */
.topnav a:hover {
background-color: #ddd;
color: black;
}
</style>
</head>
<body>
<div class="topnav">
<a href="{% url 'home' %}">Home</a>
{% if user.is_authenticated %}
<a href="{% url 'logout' %}">Logout</a>
{% else %}
<a href="{% url 'login' %}">Login</a>
{% endif %}
<a href="{% url 'signup' %}">SingUp</a>
</div>
<div class="container">
{% block content %}
{% endblock content %}
</div>
</body>
</html>
SignUp
Views.py
from django.shortcuts import render
from django.urls import reverse_lazy
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.views.generic.edit import CreateView
...
class SignUp(CreateView):
form_class = UserCreationForm
success_url = reverse_lazy("login")
template_name = "user_auth/registration/signup.html"
We create a child class from CreateView
. This class displays a form for creating an object, redisplaying the form with validation errors (if there are any), and saving the object. For this class, we give UserCreationForm
as a value to form_class
, this form creates a user, with no privileges, from the given username and password.
After the signup, the user will be taken to the login page. To template_name
we give as a value the location of the template for this view.
signup.html
{% extends 'user_auth/registration/form_base.html' %}
{% block content %}
<div class="header-bar">
<h1>Sign Up</h1>
</div>
<div class="card-body">
<form method="POST">
{% csrf_token %}
<label>{{form.username.label}}</label>
{{form.username}}
<label>{{form.password1.label}}</label>
{{form.password1}}
<label>{{form.password2.label}}</label>
{{form.password2}}
<input style="margin-top:10px;" class="button" type="submit" value="Register">
</form>
<p>Already have an account?<a href="{% url 'login' %}">Login</a></p>
</div>
{% endblock %}
Login
from django.shortcuts import render
from django.urls import reverse_lazy
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.views.generic.edit import CreateView
...
class Login(LoginView):
form_class = AuthenticationForm
template_name = "user_auth/registration/login.html"
We create a child class from LoginView
. And use AuthenticationForm
as a form_class
which is a form for logging a user in. Here we do not define success_url
because it is already defined in settings.py
.
According to the doc, this is what LoginView
does:
Heres what
LoginView
does:If called via GET, it displays a login form that POSTs to the same URL. More on this in a bit.
If called via POST with user submitted credentials, it tries to log the user in. If login is successful, the view redirects to the URL specified in next. If next isnt provided, it redirects to settings.LOGIN_REDIRECT_URL (which defaults to /accounts/profile/). If login isnt successful, it redisplays the login form.
Its your responsibility to provide the HTML for the login template , called registration/login.html by default. This template gets passed four template context variables:
form: A Form object representing the AuthenticationForm.
next: The URL to redirect to after successful login. This may contain a query string, too.
site: The current Site, according to the SITE_ID setting. If you dont have the site framework installed, this will be set to an instance of RequestSite, which derives the site name and domain from the current HttpRequest.
site_name: An alias for site.name. If you dont have the site framework installed, this will be set to the value of request.META['SERVER_NAME']. For more on sites, see The sites framework.
If youd prefer not to call the template registration/login.html, you can pass the template_name parameter via the extra arguments to the as_view method in your URLconf. For example, this URLconf line would use myapp/login.html instead:
path('accounts/login/', auth_views.LoginView.as_view(template_name='myapp/login.html')),
login.html
{% extends 'user_auth/registration/form_base.html' %}
{% block content %}
<div class="header-bar">
<h1>Login</h1>
</div>
<div class="card-body">
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<input class="button" type="submit" value="Login">
</form>
<p> Don't have an account?<a href="{% url 'signup' %}">Register</a></p>
</div>
{% endblock %}
Password Change
views.py
from django.shortcuts import render
from django.urls import reverse_lazy
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, PasswordChangeForm
from django.views.generic.edit import CreateView
from django.contrib.auth.views import LoginView, LogoutView, PasswordChangeView
...
class PasswordChange(PasswordChangeView):
form_class = PasswordChangeForm
template_name = "user_auth/registration/password_change_form.html"
The PasswordChangeView
allows the user to change this password. The PasswordChangeForm
is a form for allowing a user to change their password.
password_change.html
{% extends 'user_auth/registration/form_base.html' %}
{% block content %}
<div class="header-bar">
<h1>Password Change</h1>
</div>
<div class="card-body">
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<input class="button" type="submit" value="Login">
</form>
</div>
{% endblock %}
home.html
{% extends 'user_auth/base.html' %}
{% block content %}
<body>
<div class= "greeting">
<h1> Welcome {{ user.username|default:'' }}, to the Django Auth site.</h1>
{% if user.is_authenticated %}
<p> Change you password<a href="{% url 'password_change' %}"> Here</a></p>
{% endif %}
</div>
</body>
<style>
.greeting {
color: #F0FFFF;
text-align: center;
font-family: sans-serif;
}
</style>
{% endblock content %}
Conclusion
Django allows us to create authentication for our projects very easy using its class base views. Also, we can implement a password reset with email using PasswordResetView
and PasswordResetForm
. This is an advantage because it allows us to invest more time in our app's business logic.
Thank you for taking the time to read this article.
If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through Twitter, LinkedIn.
The source code is here.
Top comments (0)