} }

If you are building a new Django website from scratch, the first thing you implement is the User authentication. Even though Django provides a good authentication platform to use, many developers ignores it and they do re-invent the wheel.

I saw lot of beginners troubled adding user authentication for their Django projects. To remove that ambiguity, We today discuss here about a straight approach for implementing django authentication pattern.

In my experience I too did same mistakes of having a login form and POST data to a view and process it manually. But Django is so evolved that everything is available right out of the box.

Yoou can review the entire code of this article at:

https://github.com/narenaryan/django-auth-pattern

Step 1 : Understanding the Django in-built Auth system

Django's default auth system is present in the in-built app

django.contrib.auth

A Django project mainly consists of apps performing various functions. Similarly Django has many in-built apps to perform various tasks. Authentication is one main function.

If we wish to implement user authentication, better we can use the existing user model and views rather than creating our own.

The important things why it is better option

  • Login
  • Logout
  • Logout then Login
  • Password Reset
  • Password Change

All these functions are available as views in the app

django.contrib.auth

Similarly It also have the well built Forms to show the required fields in HTML template and directly process in background.

Step 2 : Adding authentication to django website from scratch

I am going to start a fresh Django project and will add login/logout to it.

$ django-admin startproject authtest
$ cd authtest
$ django-admin startapp log

So now we created a django project called authtest and added an app called log.

The project tree looks like this.

I here by use Flatly, a bootstrap flavor from shopify. I am going to add static files to my project. I already prepared them. Just download from this link. They are nothing but bootstrap and jquery files. Unzip that static folder to your project root(where manage.py lies)


https://github.com/narenaryan/website/blob/gh-pages/files/static.zip

After that create a folder called templates in project root.

$ mkdir templates

Now your project tree should look like

Yeah, now we are ready to go with modifying few settings and creating template files. Modify settings.py to add following changes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# Add static folder to STATIC_DIRS
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]

# Add templates to DIRS
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ["templates"],  #modify this line
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

Now we need to prepare a home view which we need to show when User logs-in. So let us create a view called home in log app.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#log/views.py
from django.shortcuts import render
from django.contrib.auth.decorators import login_required

# Create your views here.
# this login required decorator is to not allow to any  
# view without authenticating
@login_required(login_url="login/")
def home(request):
    return render(request,"home.html")

Now create a new file urls.py in log app and map this view

1
2
3
4
5
6
7
8
# log/urls.py
from django.conf.urls import url
from . import views

# We are adding a URL called /home
urlpatterns = [
    url(r'^$', views.home, name='home'),
]

It means whenever a user logs in successfully, he will land on /home Now we can add this to main urls.py of project

1
2
3
4
5
6
7
8
# authtest/urls.py
from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'', include('log.urls')),
]

But now the actual fun starts. We can add the in-built login view from django.contrib.auth.views into the main urls.py. It has two main methods

  • login
  • logout

We can map these views to any URL where user should signup. Since I wanted user to come to login page first I use '/login' URL. So modifying urls.py results in below code.

Adding Login to urls.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# authtest/urls.py
from django.conf.urls import include, url
from django.contrib import admin
# Add this import
from django.contrib.auth import views

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'', include('log.urls')),
    url(r'^login/$', views.login, {'template_name': 'login.html'}, 
]

Here we are forcing the django to use login.html as template for login view. Now let us create templates. One Base, Login, home templates.
templates/base.html

{% load staticfiles %}

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
                <title>Auth Demo</title>
                <!-- Core CSS - Include with every page -->
                <link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
                </head>
                <body>
                    <nav class="navbar navbar-default">
                        <div class="container-fluid">
                            <div class="navbar-header">
                                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
                                    <span class="sr-only">Toggle navigation</span>
                                    <span class="icon-bar"></span>
                                    <span class="icon-bar"></span>
                                    <span class="icon-bar"></span>
                                </button>
                                <a class="navbar-brand" href="#">Rocky Balbo</a>
                            </div>
                            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                                <ul class="nav navbar-nav navbar-right">
                                    <li class="dropdown">
                                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Account
                                            <span class="caret"></span>
                                        </a>
                                        <ul class="dropdown-menu" role="menu">
                                            <li>
                                                <a href="/home">home</a>
                                            </li>
                                            <li>
                                                <a href="#">Another action</a>
                                            </li>
                                            <li>
                                                <a href="#">Something else here</a>
                                            </li>
                                            <li class="divider"></li>
                                            <li>
                                                <a href="#">Separated link</a>
                                            </li>
                                            <li class="divider"></li>
                                            <li>
                                                <a href="/logout">Logout</a>
                                            </li>
                                        </ul>
                                    </li>
                                </ul>
                            </div>
                        </div>
                    </nav>

                {% block content %}
                {% endblock %}


                    <!-- Core Scripts - Include with every page -->
                    <script src = "{% static 'js/jquery.min.js' %}"></script>
                    <script src = "{% static 'js/bootstrap.min.js' %}"></script>
        {% block javascript %}
        {% endblock %}


            </body>
        </html>

Now let us add login.html. Don't confuse by seeing it's content. We are just using the auth form which are in-built in Django.

templates/login.html

{% extends 'base.html' %}


{% block content %}
    {% if form.errors %}

<p>Your username and password didn't match. Please try again.</p>
{% endif %}

{% if next %}
    {% if user.is_authenticated %}

<p>Your account doesn't have access to this page. To proceed,
    please login with an account that has access.</p>
    {% else %}

<p>Please login to see this page.</p>
    {% endif %}
{% endif %}

<div class="container">
    <div class="row">
        <div class="col-md-4 col-md-offset-4">
            <div class="login-panel panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">Please Sign In</h3>
                </div>
                <div class="panel-body">
                    <form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}

                        <p class="bs-component">
                            <table>
                                <tr>
                                    <td>{{ form.username.label_tag }}</td>
                                    <td>{{ form.username }}</td>
                                </tr>
                                <tr>
                                    <td>{{ form.password.label_tag }}</td>
                                    <td>{{ form.password }}</td>
                                </tr>
                            </table>
                        </p>
                        <p class="bs-component">
                            <center>
                                <input class="btn btn-success btn-sm" type="submit" value="login" />
                            </center>
                        </p>
                        <input type="hidden" name="next" value="{{ next }}" />
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

{% endblock %}

{% block javascript %}


<script>
{% if not user.is_authenticated %}
$("ul.nav.navbar-nav.navbar-right").css("display","none");
{% endif %}
</script>

{% endblock %}

Coming to home page we can keep it simple. So It consists of just a message
templates/home.html

{% extends 'base.html' %}

{% block content %}

<div class="container">
    <div class="row">
        <div class="jumbotron">
            <h1>Hello</h1>
            <p>You are on your Dashboard</p>
        </div>
    </div>
</div>
{% endblock %}

Now the elements which are rendered by Django auth.forms are plain elements. But in order to make them look like Bootstrap elements you need to add class to those elements. There is a trick of doing this. You need to create a form called LoginForm in log.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#log/forms.py
from django.contrib.auth.forms import AuthenticationForm 
from django import forms

# If you don't do this you cannot use Bootstrap CSS
class LoginForm(AuthenticationForm):
    username = forms.CharField(label="Username", max_length=30, 
                               widget=forms.TextInput(attrs={'class': 'form-control', 'name': 'username'}))
    password = forms.CharField(label="Password", max_length=30, 
                               widget=forms.TextInput(attrs={'class': 'form-control', 'name': 'password'}))

But we need to tell Django to use this extended LoginForm along with it's auth.view.login. So modify urls.py to this.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# authtest/urls.py
from django.conf.urls import include, url
from django.contrib import admin
# Add this import
from django.contrib.auth import views
from log.forms import LoginForm

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'', include('log.urls')),
    url(r'^login/$', views.login, {'template_name': 'login.html', 'authentication_form': LoginForm}),  
]

Now we need to define where user should be redirected on successful login. You should mention a setting called "LOGIN_REDIRECT_URL" to point a URL where user will move on correct login. So modify your settings file to add below line

1
LOGIN_REDIRECT_URL = '/' # It means home view

Adding Logout to urls.py

For Logout there is a view called logout that is provided by Django contrib.auth.views . We can add that too to our urls.py. But here we have a privilege of telling what URL user should redirect to.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# authtest/urls.py
from django.conf.urls import include, url
from django.contrib import admin
# Add this import
from django.contrib.auth import views
from log.forms import LoginForm

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'', include('log.urls')),
    url(r'^login/$', views.login, {'template_name': 'login.html', 'authentication_form': LoginForm}),
    url(r'^logout/$', views.logout, {'next_page': '/login'}),  
]

As you see next_page tells the page to which logout should redirected to.

Now let us migrate and run the django project.

$ python manage.py migrate
$ python manage.py createsuperuser
$ python manage.py runserver 8080

Then you visit to http://localhost:8080 and will find a nice website with

If you login with your superuser credentials, you will see the Dashboard like this

You can access the entire tutorial code here.

https://github.com/narenaryan/django-auth-pattern

In this way you can implement Register, Password Forget, Password Reset without any additional code. Django provides you everything about authentication.

If you have any queries, just mail me at narenarya@live.com

References

Thank you :). Drop a comment if you have any query at narenarya@live.com


Comments

comments powered by Disqus