Python Django ModelForms: Login Authentication, Logout and Replacing Default Forms With Custom Form Template

image.png

Picture Source

Hello everyone out there. The love I have received so far in the form of upvote from the community has motivated me a lot to create another tutorial. This time I will be showing you guys on how we can work with Django default ModelForms for login authentication and logout. Because Django default ModelForms comes with very ugly UI, we will be using our own custom template for login and registration page without having to change anything in the background processes like validation, authentication, session handling, password hashing and so on. ModelForms are great and powerful tools in Django for backend activities despite its bad UI design.

This tutorial is going to be very long and I am assuming that you guys know the core concepts of python and just a basic syntax and working of Django frameworks. Stick to the end with each details and code then you are good to go.

First of all lets create our project in dektop and I will name this project as project3. Open command prompt on your computer. You can save your project wherever you. Type in cd desktop in your cmd if you want the project to be in your desktop. Type in django-admin startproject project3 to create your project and hit enter. After that go inside your project directory by typing cd project3 and then start building your first app by typing python manage.py startapp demo. Now lets run our development server to see if everything is configured properly to start building our project. Type in python manage.py runserver. This is the overview of what have been done so far in command prompt.

Screenshot_2.png

You should see the same in cmd if everything is working as per my instructions. Go to the browser and type http://127.0.0.1:8000/. You should see the following Django default working message.

Screenshot_1.png

We will be replacing the homepage with our own simple custom template just for demo purposes. Only authorized user gets to view the homepage by logging in. New user can register and then login to view the homepage. Before I go on to create our first view i.e home view, I would really like to create all the directories and files that we will need to work on while developing this project. It will help you all to understand your directories and files structure at once so that I don't have to manually show you my project directories and files structure each time I create a new directory or a file. I will then explain on what to do inside each added files.

So first according to Django default way of rendering template convention, lets create a directory named as templates inside demo directory. And inside newly created templates directory create another new directory named as demo. Then inside newly created demo directory create three new html files named as home.html, login.html and register.html. We will use these three files for showing the view.

Also create two new python files named as urls.py and forms.py inside project3/demo directory. The first python files will let us work with URL routing and the second files will let us work with form, what fields to include and exclude, and also to create a form automatically by specifying our model. This is the overview of our project directories and files structure till now.

Screenshot_3.png

Let me show you the detailed tree like structure of our project directories and files. project3 is our top directory and this is all inside it.

│   db.sqlite3
│   manage.py
│
├───.idea
│   │   .gitignore
│   │   misc.xml
│   │   modules.xml
│   │   project3.iml
│   │
│   └───inspectionProfiles
│           profiles_settings.xml
│
├───demo
│   │   admin.py
│   │   apps.py
│   │   forms.py
│   │   models.py
│   │   tests.py
│   │   urls.py
│   │   views.py
│   │   __init__.py
│   │
│   ├───migrations
│   │       __init__.py
│   │
│   └───templates
│       └───demo
│               home.html
│               login.html
│               register.html
│
└───project3
    │   asgi.py
    │   settings.py
    │   urls.py
    │   wsgi.py
    │   __init__.py
    │
    └───__pycache__
            settings.cpython-38.pyc
            urls.cpython-38.pyc
            wsgi.cpython-38.pyc
            __init__.cpython-38.pyc

Now lets render our home template. I have a simple template created using bootstrap 4 without any external CSS files. Place this code inside your home.html.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css">
    <title>Home Page</title>
    <style type="text/css">
      a:hover{
        background-color: blue;
      }
    </style>
  </head>
  <body>
  <div class="row">
    <div class="col-md-12">
      <nav class="navbar navbar-expand-lg navbar-light bg-light navbar-dark bg-dark fixed-top">
         
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
          <span class="navbar-toggler-icon"></span>
        </button> <a class="navbar-brand keychainify-checked" href="#" style="pointer-events: none;">Demo</a>
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="navbar-nav">
            <li class="nav-item active">
               <a class="nav-link keychainify-checked" href="#">Home <span class="sr-only">(current)</span></a>
            </li>
            <li class="nav-item">
               <a class="nav-link keychainify-checked" href="#">Services</a>
            </li>
            
          </ul>
          <ul class="navbar-nav ml-md-auto">
            <li class="nav-item active">
               <a class="nav-link keychainify-checked" href="#" style="pointer-events: none">Hello <span class="sr-only">(current)</span></a>
            </li>
            <li class="nav-item active">
               <a class="nav-link keychainify-checked" href="#">Logout <span class="sr-only">(current)</span></a>
            </li>
          </ul>
        </div>
      </nav>

      <div class="row">
      <div class="jumbotron">
        <h2>
          Hello, world!
        </h2>
        <p>
          This is a template for a simple marketing or informational website. It includes a large callout called the hero unit and three supporting pieces of content. Use it as a starting point to create something more unique.
        </p>
        <p>
          <a class="btn btn-primary btn-large keychainify-checked" href="#">Learn more</a>
        </p>
      </div>
    </div>
  </div>
</div>
</div>

<div class="container-fluid">
  <div class="row">
    <div class="col-md-12">
      <div class="row">
        <div class="col-md-4">
          <div class="card">
            <img class="card-img-top" alt="Bootstrap Thumbnail First" src="https://www.layoutit.com/img/people-q-c-600-200-1.jpg" />
            <div class="card-block">
              <h5 class="card-title">
                Card title
              </h5>
              <p class="card-text">
                Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
              </p>
              <p>
                <a class="btn btn-primary keychainify-checked" href="#">Action</a> <a class="btn keychainify-checked" href="#">Action</a>
              </p>
            </div>
          </div>
        </div>
        <div class="col-md-4">
          <div class="card">
            <img class="card-img-top" alt="Bootstrap Thumbnail Second" src="https://www.layoutit.com/img/city-q-c-600-200-1.jpg" />
            <div class="card-block">
              <h5 class="card-title">
                Card title
              </h5>
              <p class="card-text">
                Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
              </p>
              <p>
                <a class="btn btn-primary keychainify-checked" href="#">Action</a> <a class="btn keychainify-checked" href="#">Action</a>
              </p>
            </div>
          </div>
        </div>
        <div class="col-md-4">
          <div class="card">
            <img class="card-img-top" alt="Bootstrap Thumbnail Third" src="https://www.layoutit.com/img/sports-q-c-600-200-1.jpg" />
            <div class="card-block">
              <h5 class="card-title">
                Card title
              </h5>
              <p class="card-text">
                Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
              </p>
              <p>
                <a class="btn btn-primary keychainify-checked" href="#">Action</a> <a class="btn keychainify-checked" href="#">Action</a>
              </p>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
  </body>
</html>


First lets go to project3/urls.py. Now lets add a URL path to point to the app which is demo in our case. First lets import include and when someone enter the homepage, we want them to point to the demo app and thereafter present the view defined in that app accordingly. So this will be the final code of project3/urls.py.

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('', include('demo.urls')),
    path('admin/', admin.site.urls),
]


So now Django knows to which app to point when someone visits the homepage. It then looks for the URLs inside our demo app. If the URL patterns matches, then it points to the corresponding views defined within that URL path. So inside demo/urls.py, lets first import path and view and then add the URLs path to specify the views function.

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name="home"),
]


Django now knows to which views to point. But we haven't created any view. So inside demo/views.py, lets create our home view.

from django.shortcuts import render

def homePage(request):
    return render(request, 'demo/home.html')


Now lets register our app inproject3/settings.py inside the list of INSTALLED_APPS.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'demo',
]


Go to your terminal. Type python manage.py runserver. There shouldn't be any error as of now. If any error appears please check your code once again or comment down below so that I can try my best to help you. Go to browser and type localhost address. Our home template should be rendered successfully and this must appears in your browser screen.

Screenshot_2.png

I have used three Bootstrap components which are Navbar, Jumbotron and Thumbnails just for the demo purposes. Now also lets create a superuser for our admin dashboard. Although there is no need of using or working with admin dashboard at this time but we are using it just for checking if our new user has been registered or not and that user appears in our database or not. So go to your terminal. Before creating a superuser you must have noticed this error in your terminal while running server: "You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions." You need to apply these migrations before working with admin model. So in the terminal type python manage.py migrate and after all migrations have been successful type python manage.py createsuperuser. Chose your desired username, email and password and then hit enter. This is the overview of what we have done so far for viewing dashboard.

image.png

Then run your server using python manage.py runserver. Go to browser and then type http://127.0.0.1:8000/admin/. You should see this one:

image.png

Type your username and password and then login to get the following output on your screen.

Screenshot_3.png

Now, the rest of this tutorial will be solely focusing on the login authentication and registration. First go to our views.py to create a registration page view. We will make a quick import of UserCreationForm. Then we will create a new instance of this UserCreationForm and then pass this instance into context dictionary. The context dictionary will then be used while rendering our Django default form template. The final code inside our views.py will be this.

from django.shortcuts import render
from django.contrib.auth.forms import UserCreationForm

def homePage(request):
    return render(request, 'demo/home.html')

def registerPage(request):
    form = UserCreationForm()
    context = {'form': form}
    return render(request, 'demo/register.html', context)


But we don't have anything inside register.html. So open that file and put this code:

<form method="post" action="">
    {% csrf_token %}
    {{form}}
    <input type="submit" value="Register Account"> 
</form>

csrf_token is used to prevent cross site scripting attack by issuing a new middleware token for each form action. Now lets add path to our register page URL. Go to demo/urls.py and add the following path to the list of urlpatterns.

urlpatterns = [
    path('', views.homePage, name="home"),
    path('register/', views.registerPage, name="register"),
]

Lets check if our server is running. Go to browser and type http://127.0.0.1:8000/register/. The following screen appears.

image.png

See how ugly is our Django ModelForms. For now, lets add little spacing to this one by printing the form as paragraph in register.html. Replace {{form}} with {{form.as_p}} and this should appear.

image.png

Now it is looking better. But we also want to add the email field, which is not here. So to do that we will modify our UserCreationForm and this is why we have created forms.py inside our demo directory. We will create a new class called CreateUserForm that will inherit from Django UserCreationForm and pass the User as a model and add an extra field called email inside the list. Before doing that we need to make quick import of ModelForm , UserCreationForm and User model. So this is the final code inside our forms.py.

from django.forms import ModelForm
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class CreateUserForm(UserCreationForm):
    class Meta:
        model =User
        fields = ['username', 'email', 'password1', 'password2']


Now go to views.py file. We need to replace the import of UserCreationForm with our newly defined CreateUserForm from our forms.py file, where we have specified new email field. Also we need to replace the instance of UserCreationForm with new instance of CreateUserForm to make email field appears in our template. This is the code to reflect the new changes.

from django.shortcuts import render
from .forms import CreateUserForm

def homePage(request):
    return render(request, 'demo/home.html')

def registerPage(request):
    form = CreateUserForm()
    context = {'form': form}
    return render(request, 'demo/register.html', context)


Now refresh your browser to see the following output.

image.png

Its time to add some functionality to this registration page. We need to save newly register user to our database. So inside our views.py, lets add those functionality. If request method is post then we will pass those post data to CreateUserForm and if validation is all correct then we will actually save those form data and then redirect to homepage for now. This is reflected by this code:

from django.shortcuts import render, redirect
from .forms import CreateUserForm

def homePage(request):
    return render(request, 'demo/home.html')

def registerPage(request):
    form = CreateUserForm()
    if request.method == "POST":
        form = CreateUserForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('/')
    context = {'form': form}
    return render(request, 'demo/register.html', context)


Now lets test our code. I typed in following and then registered it.

image.png

Lets check our dashboard to see if user has been saved to database.

image.png

Ok well our new user has been registered then. We had not encountered any errors upto this point. Why not check the validation. I will intentionally try to register with incorrect password confirmation. This is the result I got.

Screenshot_1.png

Lets remove annoying bullet point of this form. To do this inside register.html we will replace {{form}} the following code.

<form method="post" action="">
    {% csrf_token %}
    {{form.username.label}}
    {{form.username}}

    {{form.email.label}}
    {{form.email}}

    {{form.password1.label}}
    {{form.password1}}

    {{form.password2.label}}
    {{form.password2}}

    <input type="submit" value="Register Account">
</form>


Our form should look like this now.

image.png

Understanding few things over here will help us understand how to replace this form with our custom form. For example {{form.username.label}} is showing the Username in the screen and {{form.username}} shows the input box to type. So in our custom form we will replace the input type with this {{form.username}}. And we are good to go. So, I already have a template designed for the form. You can copy this code inside your register.html. I will just paste my final code of register.html here by replacing those input types with what I've explained before.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">


    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css">

    <title>Registration</title>

    <style>

      body{
        background: rgba(0, 0, 0, 0.85);
      }
    </style>

  </head>
  <body>
  <div class="row">
    <div class="col-md-8 m-auto pt-3">
      <div class="card bg-dark m-auto" style="width: 60%; box-shadow: 10px 10px 15px white;">
        <div class="card-title text-center">
          <h2 class="mt-3 text-white">Create An Account</h3>
          <i class="fa fa-shopping-cart fa-10x mt-3" style="color: #5bc0de"></i>
        </div>
        <div class="card-body">
          <form method="post" action="">
              {% csrf_token %}
            <div class="input-group mb-2">
              <button class="btn btn-info" style="border-radius: 0; pointer-events: none;"><i class="fa fa-user fa-2x"></i></button>
                {{form.username}}
            </div>
            <div class="input-group mb-2">
              <button class="btn btn-info" style="border-radius: 0; pointer-events: none;"><i class="fa fa-envelope fa-2x"></i></button>
                {{form.email}}
            </div>
            <div class="input-group mb-2">
              <button class="btn btn-info" style="border-radius: 0; pointer-events: none;"><i class="fa fa-key fa-2x"></i></button>
                {{form.password1}}
            </div>
            <div class="input-group mb-2">
              <button class="btn btn-info" style="border-radius: 0; pointer-events: none;"><i class="fa fa-key fa-2x"></i></button>
                {{form.password2}}
            </div>
             <button class="btn btn-info" style="border-radius: 0;">Register</button>
            <h6 class="text-white">Already have an account? <a class="btn btn-info" href="{% url 'login' %}">Login Instead</a></h6>
          </form>
            {% if form.errors %}
            <button class="btn btn-info">{{form.errors}}</button>
            {% endif %}
        </div>
      </div>
    </div>
  </div>


    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
  <script>
        var form_fields = document.getElementsByTagName('input')
        form_fields[1].placeholder='Username..';
        form_fields[2].placeholder='Email..';
        form_fields[3].placeholder='Enter password...';
        form_fields[4].placeholder='Re-enter Password...';


        for (var field in form_fields){
            form_fields[field].className += ' form-control py-4'
        }
    </script>
  </body>
</html>

Run your server and see in the browser how it looks like. This is the output.

image.png

Lets check the validation. Lets type the wrong password confirmation field.

image.png

Ok our errors are showing too. When users enter all fields correctly we want them to redirect to login page. So lets add code to our login.html file (Copy paste from here).

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">


    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css">

    <title>Login</title>

    <style>

      body{
        background: rgba(0, 0, 0, 0.85);
      }
    </style>

  </head>
  <body>
  <div class="row pt-4">
    <div class="col-md-8 m-auto pt-5">
      <div class="card bg-dark m-auto" style="width: 60%; box-shadow: 10px 10px 15px white;">
        <div class="card-title text-center">
          <h2 class="mt-3 text-white">Login to Your Account</h3>
          <i class="fa fa-shopping-cart fa-10x mt-3" style="color: #5bc0de"></i>
        </div>
        <div class="card-body">
          <form action="" method="post">
            {% csrf_token %}
            <div class="input-group mb-2">
              <button class="btn btn-info" style="border-radius: 0; pointer-events: none;"><i class="fa fa-user fa-2x"></i></button>
              <input type="text" class="form-control py-4" placeholder="Username" name="username">
            </div>
            <div class="input-group mb-2">
              <button class="btn btn-info" style="border-radius: 0; pointer-events: none;"><i class="fa fa-key fa-2x"></i></button>
              <input type="password" class="form-control py-4" placeholder="Password" name="password">
            </div>
            <button class="btn btn-info" style="border-radius: 0;">Login</button>
            <h6 class="text-white">Don't have an account? <a class="btn btn-info" href="{% url 'register'%}">Signup Instead</a></h6>
            
            

          </form>
        </div>
      </div>
    </div>
  </div>


    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
  </body>
</html>

Lets add path to our urls.py for login and specify the view to be pointed for it and then actually create a loginPage view too. So our final urls.py is:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.homePage, name="home"),
    path('register/', views.registerPage, name="register"),
    path('login/', views.loginPage, name="login")
]



To our view when someone register we want them to redirect to login page. So our views.py is:

from django.shortcuts import render, redirect
from .forms import CreateUserForm

def homePage(request):
    return render(request, 'demo/home.html')

def loginPage(request):
    return render(request, 'demo/login.html')

def registerPage(request):
    form = CreateUserForm()
    if request.method == "POST":
        form = CreateUserForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('login')
    context = {'form': form}
    return render(request, 'demo/register.html', context)

Type http://127.0.0.1:8000/login/ in your browser and the following screen appears.

image.png

We have already added dynamic URL routing above. You should be able to visit login page from the registration page and vice versa. The syntax for url routing is {% url '<Name you defined in the urls.py for the path>' %}. Also when a new user is registered we want to redirect them to login page and show them the message that the user has been registered. For this go to views.py and make a quick import of messages from django.contrib.

from django.shortcuts import render, redirect
from .forms import CreateUserForm
from django.contrib import messages

def homePage(request):
    return render(request, 'demo/home.html')

def loginPage(request):
    return render(request, 'demo/login.html')

def registerPage(request):
    form = CreateUserForm()
    if request.method == "POST":
        form = CreateUserForm(request.POST)
        if form.is_valid():
            form.save()
            user = form.cleaned_data.get('username')
            messages.success(request, 'Account was created for ' + user)
            return redirect('login')
    context = {'form': form}
    return render(request, 'demo/register.html', context)

Then in login.html, we will display the message after login button and before register button. So write this code just below the login button or just below this <button class="btn btn-info" style="border-radius: 0;">Login</button>.

{% for message in messages %}
              <p class="text-white">{{message}}</p>
            {% endfor %}

Now check our code. I will register a new user.

Screenshot_2.png

Then it is redirected to the login page saying the account for that user has been created. You can check your dashboard to see the user has been created too.

Screenshot_3.png

Now time for that user to login with the credentials provided in registration and then authenticating that user to the homepage. So inside views.py, lets add this code to our loginPage view.

def loginPage(request):
    if request.method == "POST":
        username = request.POST.get('username')
        password = request.POST.get('password')

        user = authenticate(request, username=username, password=password)

        if user is not None:
            login(request, user)
            return redirect('/')
        else:
            messages.info(request, 'Username or Password is incorrect.')

    return render(request, 'demo/login.html')


You can check all the validations are working correctly. If you try to login with wrong credentials it will display message that "Username or password is incorrect". The logged in user gets to visit the home page too. Now time to work with logout. In views.py lets add this logout view.

def logoutPage(request):
    logout(request)
    return redirect('login')

And add the path to URL for the same.

from django.urls import path
from . import views

urlpatterns = [
    path('', views.homePage, name="home"),
    path('register/', views.registerPage, name="register"),
    path('login/', views.loginPage, name="login"),
    path('logout/', views.logoutPage, name="logout")
]

Inside our home.html, we want to display the name of logged in user and while they click on logout. that should redirect them to login page. So lets add URL routing to that too. This is our final code inside home.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css">
    <title>Home Page</title>
    <style type="text/css">
      a:hover{
        background-color: blue;
      }
    </style>
  </head>
  <body>
  <div class="row">
    <div class="col-md-12">
      <nav class="navbar navbar-expand-lg navbar-light bg-light navbar-dark bg-dark fixed-top">

        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
          <span class="navbar-toggler-icon"></span>
        </button> <a class="navbar-brand keychainify-checked" href="#" style="pointer-events: none;">Demo</a>
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="navbar-nav">
            <li class="nav-item active">
               <a class="nav-link keychainify-checked" href="#">Home <span class="sr-only">(current)</span></a>
            </li>
            <li class="nav-item">
               <a class="nav-link keychainify-checked" href="#">Services</a>
            </li>

          </ul>
          <ul class="navbar-nav ml-md-auto">
            <li class="nav-item active">
               <a class="nav-link keychainify-checked" href="#" style="pointer-events: none">Hello {{request.user}} <span class="sr-only">(current)</span></a>
            </li>
            <li class="nav-item active">
               <a class="nav-link keychainify-checked" href="{% url 'logout' %}">Logout <span class="sr-only">(current)</span></a>
            </li>
          </ul>
        </div>
      </nav>

      <div class="row">
      <div class="jumbotron">
        <h2>
          Hello, world!
        </h2>
        <p>
          This is a template for a simple marketing or informational website. It includes a large callout called the hero unit and three supporting pieces of content. Use it as a starting point to create something more unique.
        </p>
        <p>
          <a class="btn btn-primary btn-large keychainify-checked" href="#">Learn more</a>
        </p>
      </div>
    </div>
  </div>
</div>
</div>

<div class="container-fluid">
  <div class="row">
    <div class="col-md-12">
      <div class="row">
        <div class="col-md-4">
          <div class="card">
            <img class="card-img-top" alt="Bootstrap Thumbnail First" src="https://www.layoutit.com/img/people-q-c-600-200-1.jpg" />
            <div class="card-block">
              <h5 class="card-title">
                Card title
              </h5>
              <p class="card-text">
                Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
              </p>
              <p>
                <a class="btn btn-primary keychainify-checked" href="#">Action</a> <a class="btn keychainify-checked" href="#">Action</a>
              </p>
            </div>
          </div>
        </div>
        <div class="col-md-4">
          <div class="card">
            <img class="card-img-top" alt="Bootstrap Thumbnail Second" src="https://www.layoutit.com/img/city-q-c-600-200-1.jpg" />
            <div class="card-block">
              <h5 class="card-title">
                Card title
              </h5>
              <p class="card-text">
                Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
              </p>
              <p>
                <a class="btn btn-primary keychainify-checked" href="#">Action</a> <a class="btn keychainify-checked" href="#">Action</a>
              </p>
            </div>
          </div>
        </div>
        <div class="col-md-4">
          <div class="card">
            <img class="card-img-top" alt="Bootstrap Thumbnail Third" src="https://www.layoutit.com/img/sports-q-c-600-200-1.jpg" />
            <div class="card-block">
              <h5 class="card-title">
                Card title
              </h5>
              <p class="card-text">
                Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
              </p>
              <p>
                <a class="btn btn-primary keychainify-checked" href="#">Action</a> <a class="btn keychainify-checked" href="#">Action</a>
              </p>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
  </body>
</html>


See the output for yourself. The name has been displayed and you will be rediected to login page if you click logout.

image.png

The final thing remains. While the user is logged in and at the same time you type http://127.0.0.1:8000/login/ and http://127.0.0.1:8000/register/ then it will show you login page and register page respectively. This is a very bad functionality. Also without being logged in when you type http://127.0.0.1:8000, it will show you homepage. So for this in our views.py, we need to import login_required. And then use this decorator on our homepage view function to restrict anyone from viewing homepage without logging in. And only authenticated user gets to see the homepage. So our final views.py will look something like this.

from django.shortcuts import render, redirect
from .forms import CreateUserForm
from django.contrib import messages
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required

@login_required(login_url='login')
def homePage(request):
    return render(request, 'demo/home.html')

def loginPage(request):
    if request.user.is_authenticated:
        return redirect('home')
    else:
        if request.method == "POST":
            username = request.POST.get('username')
            password = request.POST.get('password')

            user = authenticate(request, username=username, password=password)

            if user is not None:
                login(request, user)
                return redirect('/')
            else:
                messages.info(request, 'Username or Password is incorrect.')

        return render(request, 'demo/login.html')

def logoutPage(request):
    logout(request)
    return redirect('login')

def registerPage(request):
    if request.user.is_authenticated:
        return redirect('home')
    else:
        form = CreateUserForm()
        if request.method == "POST":
            form = CreateUserForm(request.POST)
            if form.is_valid():
                form.save()
                user = form.cleaned_data.get('username')
                messages.success(request, 'Account was created for ' + user)
                return redirect('login')
        context = {'form': form}
        return render(request, 'demo/register.html', context)

Finally our project has been completed. Go to your homepage, you will see that you need to log in. While you are logged in and you type in login page URL, it will show you the home page because you are already authenticated.

This is the final demo on my laptop about the same project.
output.gif

Download the project source code here.

Getting Started

Username: testuser
Password: testuser123

4 Comments