Summary: in this tutorial, you’ll learn how to use the Django LoginView
to create a login page for the Todo App.
This tutorial begins where the Django DeleteView tutorial left off.
Django’s LoginView
allows you to display the login form and process the login action. We’ll use the LoginView
class to create a login page for the Todo App.
You can download the final code of this tutorial here.
Creating & configuring users app for the todo project
The users
app will have the following functionalities:
- Login / Logout
- Register
- Reset password
In this tutorial, we’ll focus on the Login / Logout functions.
First, use the startapp
command to create the users
app:
django-admin startapp users
Code language: Python (python)
Next, register the users
app in the settings.py
of the project:
INSTALLED_APPS = [
#...
'users'
]
Code language: Python (python)
Then, create urls.py
in the users
app with the following code:
from django.urls import path
urlpatterns = []
Code language: Python (python)
After that, include the
of the urls.py
users
app in the
of the project:urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('todo.urls')),
path('',include('users.urls'))
]
Code language: Python (python)
Finally, create the templates
directory, and users
directory inside the templates
directory in the users
app to store the templates.
Creating a login page
The following defines the My
class in the LoginView
views.py
file that inherits from the LoginView
class:
from django.contrib.auth.views import LoginView
from django.urls import reverse_lazy
from django.contrib import messages
class MyLoginView(LoginView):
redirect_authenticated_user = True
def get_success_url(self):
return reverse_lazy('tasks')
def form_invalid(self, form):
messages.error(self.request,'Invalid username or password')
return self.render_to_response(self.get_context_data(form=form))
Code language: Python (python)
How it works.
First, import the LoginView
from the
.django.contrib
auth.views
, reverse_lazy
from django.urls
, and messages from django.contrib
from django.contrib.auth.views import LoginView
from django.urls import reverse_lazy
from django.contrib import messages
Code language: Python (python)
Second, define the My
class that inherits from the LoginView
LoginView
class. The My
has the following attributes and methods:LoginView
is set toredirect_authenticated_user
True
to instruct Django to redirect the users once they log in successfully. By default, the
isredirect_authenticated_user
False
, which turns off the redirection.get_success_url()
returns the URL to redirect after the users log in successfully.
is called once the login is failed. In theform_invalid()
, we create a flash message and rerender the login form.form_invalid()
Defining a route for the login page
Modify the views.py
file to define a route for the login page:
from django.urls import path
from .views import MyLoginView
urlpatterns = [
path('login/', MyLoginView.as_view(),name='login'),
]
Code language: Python (python)
How it works.
First, import the MyLoginView
class from the views.py
:
from .views import MyLoginView
Code language: Python (python)
Second, map the login/
route to the result of as_view()
method of the MyLoginView
class.
urlpatterns = [
path('login/', MyLoginView.as_view(),name='login'),
]
Code language: Python (python)
Creating a login template
First, create the login.html
template in the templates/users
directory with the following code:
{%extends 'base.html'%}
{%block content%}
<div class="center">
<form method="post" class="card" novalidate>
{% csrf_token %}
<h2 class="text-center">Log in to your account</h2>
{% for field in form %}
{{ field.label_tag }}
{{ field }}
{% if field.errors %}
<small>{{ field.errors|striptags }}</small>
{% endif %}
{% endfor %}
<input type="submit" value="Login" class="btn btn-primary full-width">
<hr>
<p class="text-center">Forgot your password <a href="#">Reset Password</a></p>
<p class="text-center">Don't have a account? <a href="#">Join Now</a></p>
</form>
</div>
{%endblock content%}
Code language: HTML, XML (xml)
If you open the login URL:
http://127.0.0.1:8000/login/
Code language: Python (python)
you’ll see the following login form:
If you enter a valid username and password, you’ll log in successfully. Otherwise, you’ll get a message saying you have entered an invalid username or password.
Second, add the LOGIN_URL
to the settings.py
of the project:
LOGIN_URL = 'login'
Code language: Python (python)
If you attempt to access a page that requires login, Django will use this
for redirection. If you don’t add the LOGIN_URL
to the LOGIN_URL
settings.py
, Django will use the default login URL which is accounts/login/
.
Creating a logout URL
The
logs a user out and displays a message. We’ll use the LogoutView
to create a logout link.LogoutView
Unlike the LoginView
class, you can use the LogoutView
class directly in the urls.py
. For example, you can modify the views.py
to create a route for the logout URL:
from django.urls import path
from .views import MyLoginView
from django.contrib.auth.views import LogoutView
urlpatterns = [
path('login/', MyLoginView.as_view(),name='login'),
path('logout/', LogoutView.as_view(next_page='login'),name='logout'),
]
Code language: Python (python)
How it works.
First, import the LogoutView
from the django.contrib
.auth.views
:
from django.contrib.auth.views import LogoutView
Code language: Python (python)
Second, map the URL logout/
with the result of the as_view()
method of the LogoutView
class. The next_page
argument specifies the URL that the users will be redirected to once they log out successfully.
path('logout/', LogoutView.as_view(next_page='login'),name='logout')
Code language: Python (python)
Adding login/logout links to the header
If the user logs in, the header shows the home, my tasks, new task, and logout link. Once the user logs out, the header displays the home, login, and join now links.
To achieve this, you modify the base.html
template of the project as follows.
{%load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static 'css/style.css' %}" />
<title>Todo List</title>
</head>
<body>
<header class="header">
<div class="container">
<a href="{%url 'home'%}" class="logo">Todo</a>
<nav class="nav">
<a href="{%url 'home'%}"><i class="bi bi-house-fill"></i> Home</a>
{% if request.user.is_authenticated %}
<a href="{% url 'tasks' %}"><i class="bi bi-list-task"></i> My Tasks</a>
<a href="{% url 'task-create' %}"><i class="bi bi-plus-circle"></i> Create Task</a>
<a href="#">Hi {{request.user | title}}</a>
<a href="{% url 'logout' %}" class="btn btn-outline">Logout</a>
{% else %}
<a href="{% url 'login' %}" class="btn btn-outline">Login</a>
<a href="#" class="btn btn-primary">Join Now</a>
{% endif %}
</nav>
</div>
</header>
<main>
<div class="container">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{message.tags}}">
{{message}}
</div>
{% endfor %}
{% endif %}
{%block content %}
{%endblock content%}
</div>
</main>
<footer class="footer">
<div class="container">
<p>© Copyright {% now "Y" %} by <a href="https://www.pythontutorial.net">Python Tutorial</a></p>
</div>
</footer>
</body>
</html>
Code language: HTML, XML (xml)
Note that if the user logs in, the request.user.is_authenticated
returns True
. Therefore, you can use this property to check whether a user is logged or not.
If you are not logged in, you’ll see the following links in the navigation:
However, if you log in, you’ll see the following navigation links:
LoginRequiredMixin
Although you’re not logged in, you still can manage the task list such as viewing, adding, editing, and deleting the tasks. To protect these pages, you’ll use the LoginRequiredMixin
class.
To do that, you modify the views.py
of the todo application and use the LoginRequiredMixin
class as follows:
from django.shortcuts import render
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.contrib import messages
from django.urls import reverse_lazy
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import Task
class TaskDelete(LoginRequiredMixin, DeleteView):
model = Task
context_object_name = 'task'
success_url = reverse_lazy('tasks')
def form_valid(self, form):
messages.success(self.request, "The task was deleted successfully.")
return super(TaskDelete,self).form_valid(form)
class TaskUpdate(LoginRequiredMixin, UpdateView):
model = Task
fields = ['title','description','completed']
success_url = reverse_lazy('tasks')
def form_valid(self, form):
messages.success(self.request, "The task was updated successfully.")
return super(TaskUpdate,self).form_valid(form)
class TaskCreate(LoginRequiredMixin, CreateView):
model = Task
fields = ['title','description','completed']
success_url = reverse_lazy('tasks')
def form_valid(self, form):
form.instance.user = self.request.user
messages.success(self.request, "The task was created successfully.")
return super(TaskCreate,self).form_valid(form)
class TaskDetail(LoginRequiredMixin, DetailView):
model = Task
context_object_name = 'task'
class TaskList(LoginRequiredMixin,ListView):
model = Task
context_object_name = 'tasks'
def home(request):
return render(request,'home.html')
Code language: Python (python)
If you have not logged in and attempted to access a protected page, Django will redirect you to the login page. For example:
http://127.0.0.1:8000/task/create
Code language: Python (python)
Django will redirect you to the login page using the LOGIN_URL
configured in the settings.py
:
http://127.0.0.1:8000/login/?next=/task/create/
Code language: Python (python)
Summary
- Use the
LoginView
class to create a login page. - Use the
LogoutView
class to log a user out. - Use the
LoginRequiredMixin
class to protect a page.