The authentication module saves a lot of time in creating space for users. The following are the main advantages of this module:
- The main actions related to users are simplified (connection, account activation, and so on)
- Using this system ensures a certain level of security
- Access restrictions to pages can be done very easily
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 chapter greatly alters the application we wrote earlier. At the end of this chapter, we will have:
- Modified our UserProfile model to make it compatible with the module
- Created a login page
- Modified the addition of developer and supervisor pages
- Added the restriction of access to connected users
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 and it is not the subject of the book. To reset South, we must do the following:
- Delete the
TasksManager/migrations
folder and all the files contained in this folder - Delete the
database.db
file
To use the migration system, we have to use the following commands already used in the chapter about models:
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 password")
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:
- First, we must import the decorator with the following line:
from django.contrib.auth.decorators import login_required
- Then, we will add the decorator just before the declaration of the view:
@login_required def page(request): # This line already exists. Do not copy it.
- With the addition of these two lines, the page that lets you add a developer is only available to the logged-in users. If you try to access the page without being connected, you will realize that it is not very practical because the obtained page is a 404 error. To improve this, simply tell Django what the connection URL is by adding the following line in the
settings.py
file:LOGIN_URL = 'public_connection'
- With this line, if the user tries to access a protected page, he/she will be redirected to the login page. You may have noticed that if you're not logged in and you click the Create a developer link, the URL contains a parameter named next. The following is the screen capture of the URL:
- This parameter contains the URL that the user tried to consult. The authentication module redirects the user to the page when he/she connects. To do this, we will modify the
connection.py
file we created. We add the line that imports therender()
function to import theredirect()
function:from django.shortcuts import render, redirect
- To redirect the user after they log in, we must add two lines after the line that contains the code login (request, user). There are two lines to be added:
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.
Restricting access to views
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:
- First, we must import the decorator with the following line:
from django.contrib.auth.decorators import login_required
- Then, we will add the decorator just before the declaration of the view:
@login_required def page(request): # This line already exists. Do not copy it.
- With the addition of these two lines, the page that lets you add a developer is only available to the logged-in users. If you try to access the page without being connected, you will realize that it is not very practical because the obtained page is a 404 error. To improve this, simply tell Django what the connection URL is by adding the following line in the
settings.py
file:LOGIN_URL = 'public_connection'
- With this line, if the user tries to access a protected page, he/she will be redirected to the login page. You may have noticed that if you're not logged in and you click the Create a developer link, the URL contains a parameter named next. The following is the screen capture of the URL:
- This parameter contains the URL that the user tried to consult. The authentication module redirects the user to the page when he/she connects. To do this, we will modify the
connection.py
file we created. We add the line that imports therender()
function to import theredirect()
function:from django.shortcuts import render, redirect
- To redirect the user after they log in, we must add two lines after the line that contains the code login (request, user). There are two lines to be added:
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.
Restricting access to URLs
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 chapter, 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.
In the next chapter, we will improve the usability of the application with the addition of AJAX requests. We will learn the basics of jQuery and then learn how to use it to make an asynchronous request to the server. Also, we will learn how to handle the response from the server.