The authentication module saves a lot of time in creating space for users. The following are the main advantages of this module:
(For more resources related to this topic, see here.)
It's such a useful module that we have already used it without noticing. Indeed, access to the administration module is performed by the authentication module. The user we created during the generation of our database was the first user of the site.
This article greatly alters the application we wrote earlier. At the end of this article, we will have:
In this section, we will learn how to use the authentication module by making our application compatible with the module.
There is normally nothing special to do for the administration module to work in our TasksManager application. Indeed, by default, the module is enabled and allows us to use the administration module. However, it is possible to work on a site where the web Django authentication module has been disabled. We will check whether the module is enabled.
In the INSTALLED_APPS section of the settings.py file, we have to check the following line:
'django.contrib.auth',
The authentication module has its own User model. This is also the reason why we have created a UserProfile model and not just User. It is a model that already contains some fields, such as nickname and password. To use the administration module, you have to use the User model on the Python33/Lib/site-package/django/contrib/auth/models.py file.
We will modify the UserProfile model in the models.py file that will become the following:
class UserProfile(models.Model):
user_auth = models.OneToOneField(User, primary_key=True)
phone = models.CharField(max_length=20, verbose_name="Phone number", null=True, default=None, blank=True)
born_date = models.DateField(verbose_name="Born date", null=True, default=None, blank=True)
last_connexion = models.DateTimeField(verbose_name="Date of last connexion", null=True, default=None, blank=True)
years_seniority = models.IntegerField(verbose_name="Seniority", default=0)
def __str__(self):
return self.user_auth.username
We must also add the following line in models.py:
from django.contrib.auth.models import User
In this new model, we have:
The OneToOne relation means that for each recorded UserProfile model, there will be a record of the User model. In doing all this, we deeply modify the database. Given these changes and because the password is stored as a hash, we will not perform the migration with South.
It is possible to keep all the data and do a migration with South, but we should develop a specific code to save the information of the UserProfile model to the User model. The code should also generate a hash for the password, but it would be long. To reset South, we must do the following:
To use the migration system, we have to use the following commands:
manage.py schemamigration TasksManager --initial
manage.py syncdb –migrate
After the deletion of the database, we must remove the initial data in create_developer.py. We must also delete the URL developer_detail and the following line in index.html:
<a href="{% url "developer_detail" "2" %}">Detail second developer (The second user must be a developer)</a><br />
The pages that allow you to add a developer and supervisor no longer work because they are not compatible with our recent changes. We will change these pages to integrate our style changes. The view contained in the create_supervisor.py file will contain the following code:
from django.shortcuts import render
from TasksManager.models import Supervisor
from django import forms
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
def page(request):
if request.POST:
form = Form_supervisor(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
login = form.cleaned_data['login']
password = form.cleaned_data['password']
specialisation = form.cleaned_data['specialisation']
email = form.cleaned_data['email']
new_user = User.objects.create_user(username = login, email = email, password=password)
# In this line, we create an instance of the User model with the create_user() method. It is important to use this method because it can store a hashcode of the password in database. In this way, the password cannot be retrieved from the database. Django uses the PBKDF2 algorithm to generate the hash code password of the user.
new_user.is_active = True
# In this line, the is_active attribute defines whether the user can connect or not. This attribute is false by default which allows you to create a system of account verification by email, or other system user validation.
new_user.last_name=name
# In this line, we define the name of the new user.
new_user.save()
# In this line, we register the new user in the database.
new_supervisor = Supervisor(user_auth = new_user, specialisation=specialisation)
# In this line, we create the new supervisor with the form data. We do not forget to create the relationship with the User model by setting the property user_auth with new_user instance.
new_supervisor.save()
return HttpResponseRedirect(reverse('public_empty'))
else:
return render(request, 'en/public/create_supervisor.html', {'form' : form})
else:
form = Form_supervisor()
form = Form_supervisor()
return render(request, 'en/public/create_supervisor.html', {'form' : form})
class Form_supervisor(forms.Form):
name = forms.CharField(label="Name", max_length=30)
login = forms.CharField(label = "Login")
email = forms.EmailField(label = "Email")
specialisation = forms.CharField(label = "Specialisation")
password = forms.CharField(label = "Password", widget = forms.PasswordInput)
password_bis = forms.CharField(label = "Password", widget = forms.PasswordInput)
def clean(self):
cleaned_data = super (Form_supervisor, self).clean()
password = self.cleaned_data.get('password')
password_bis = self.cleaned_data.get('password_bis')
if password and password_bis and password != password_bis:
raise forms.ValidationError("Passwords are not identical.")
return self.cleaned_data
The create_supervisor.html template remains the same, as we are using a Django form.
You can change the page() method in the create_developer.py file to make it compatible with the authentication module (you can refer to downloadable Packt code files for further help):
def page(request):
if request.POST:
form = Form_inscription(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
login = form.cleaned_data['login']
password = form.cleaned_data['password']
supervisor = form.cleaned_data['supervisor']
new_user = User.objects.create_user(username = login, password=password)
new_user.is_active = True
new_user.last_name=name
new_user.save()
new_developer = Developer(user_auth = new_user, supervisor=supervisor)
new_developer.save()
return HttpResponse("Developer added")
else:
return render(request, 'en/public/create_developer.html', {'form' : form})
else:
form = Form_inscription()
return render(request, 'en/public/create_developer.html', {'form' : form})
We can also modify developer_list.html with the following content:
{% extends "base.html" %}
{% block title_html %}
Developer list
{% endblock %}
{% block h1 %}
Developer list
{% endblock %}
{% block article_content %}
<table>
<tr>
<td>Name</td>
<td>Login</td>
<td>Supervisor</td>
</tr>
{% for dev in object_list %}
<tr>
<!-- The following line displays the __str__ method of the model. In this case it will display the username of the developer -->
<td><a href="">{{ dev }}</a></td>
<!-- The following line displays the last_name of the developer -->
<td>{{ dev.user_auth.last_name }}</td>
<!-- The following line displays the __str__ method of the Supervisor model. In this case it will display the username of the supervisor -->
<td>{{ dev.supervisor }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
Now that you can create users, you must create a login page to allow the user to authenticate. We must add the following URL in the urls.py file:
url(r'^connection$', 'TasksManager.views.connection.page', name="public_connection"),
You must then create the connection.py view with the following code:
from django.shortcuts import render
from django import forms
from django.contrib.auth import authenticate, login
# This line allows you to import the necessary functions of the authentication module.
def page(request):
if request.POST:
# This line is used to check if the Form_connection form has been posted. If mailed, the form will be treated, otherwise it will be displayed to the user.
form = Form_connection(request.POST)
if form.is_valid():
username = form.cleaned_data["username"]
password = form.cleaned_data["password"]
user = authenticate(username=username, password=password)
# This line verifies that the username exists and the password is correct.
if user:
# In this line, the authenticate function returns None if authentication has failed, otherwise it returns an object that validates the condition.
login(request, user)
# In this line, the login() function allows the user to connect.
else:
return render(request, 'en/public/connection.html', {'form' : form})
else:
form = Form_connection()
return render(request, 'en/public/connection.html', {'form' : form})
class Form_connection(forms.Form):
username = forms.CharField(label="Login")
password = forms.CharField(label="Password", widget=forms.PasswordInput)
def clean(self):
cleaned_data = super(Form_connection, self).clean()
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if not authenticate(username=username, password=password):
raise forms.ValidationError("Wrong login or passwsord")
return self.cleaned_data
You must then create the connection.html template with the following code:
{% extends "base.html" %}
{% block article_content %}
{% if user.is_authenticated %}
<-- This line checks if the user is connected.-->
<h1>You are connected.</h1>
<p>
Your email : {{ user.email }}
<-- In this line, if the user is connected, this line will display his/her e-mail address.-->
</p>
{% else %}
<!-- In this line, if the user is not connected, we display the login form.-->
<h1>Connexion</h1>
<form method="post" action="{{ public_connection }}">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" class="button" value="Connection" />
</form>
{% endif %}
{% endblock %}
When the user logs in, Django will save his/her data connection in session variables. This example has allowed us to verify that the audit login and password was transparent to the user. Indeed, the authenticate() and login() methods allow the developer to save a lot of time. Django also provides convenient shortcuts for the developer such as the user.is_authenticated attribute that checks if the user is logged in. Users prefer when a logout link is present on the website, especially when connecting from a public computer. We will now create the logout page.
First, we need to create the logout.py file with the following code:
from django.shortcuts import render
from django.contrib.auth import logout
def page(request):
logout(request)
return render(request, 'en/public/logout.html')
In the previous code, we imported the logout() function of the authentication module and used it with the request object. This function will remove the user identifier of the request object, and delete flushes their session data.
When the user logs out, he/she needs to know that the site was actually disconnected. Let's create the following template in the logout.html file:
{% extends "base.html" %}
{% block article_content %}
<h1>You are not connected.</h1>
{% endblock %}
When developers implement an authentication system, it's usually to limit access to anonymous users. In this section, we'll see two ways to control access to our web pages.
The authentication module provides simple ways to prevent anonymous users from accessing some pages. Indeed, there is a very convenient decorator to restrict access to a view. This decorator is called login_required.
In the example that follows, we will use the designer to limit access to the page() view from the create_developer module in the following manner:
from django.contrib.auth.decorators import login_required
@login_required
def page(request): # This line already exists. Do not copy it.
LOGIN_URL = 'public_connection'
from django.shortcuts import render, redirect
if request.GET.get('next') is not None:
return redirect(request.GET['next'])
This system is very useful when the user session has expired and he/she wants to see a specific page.
The system that we have seen does not simply limit access to pages generated by CBVs. For this, we will use the same decorator, but this time in the urls.py file.
We will add the following line to import the decorator:
from django.contrib.auth.decorators import login_required
We need to change the line that corresponds to the URL named create_project:
url (r'^create_project$', login_required(CreateView.as_view(model=Project, template_name="en/public/create_project.html", success_url = 'index')), name="create_project"),
The use of the login_required decorator is very simple and allows the developer to not waste too much time.
In this article, we modified our application to make it compatible with the authentication module. We created pages that allow the user to log in and log out. We then learned how to restrict access to some pages for the logged in users.
To learn more about Django, the following books published by Packt Publishing (https://www.packtpub.com/) are recommended:
Django By Example (https://www.packtpub.com/web-development/django-example)
Learning Django Web Development (https://www.packtpub.com/web-development/learning-django-web-development)
Further resources on this subject: