A Guide to User Registration, Login, and Logout in Django

Aug. 12, 2020, 3:28 p.m.
Django · 10 min read
A Guide to User Registration, Login, and Logout in Django
Last Modified: July 5, 2021, 4:12 p.m.

This article will cover how to allow user registration, login, and logout functionality on a site built using the Django Web Framework. 

Before we begin, the virtual environment is called env, the Django project is called mysite, and the app is called main.

 


 

User registration

As you may have seen, Django comes with a built-in user registration form. We just need to configure it to our needs (i.e. collect an email address upon registration). 

 

Create the register form

env > mysite > main > (New File) forms.py

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


# Create your forms here.

class NewUserForm(UserCreationForm):
	email = forms.EmailField(required=True)

	class Meta:
		model = User
		fields = ("username", "email", "password1", "password2")

	def save(self, commit=True):
		user = super(NewUserForm, self).save(commit=False)
		user.email = self.cleaned_data['email']
		if commit:
			user.save()
		return user

Django comes with a pre-built register form called UserCreationForm that connects to the pre-built model UserHowever, the UserCreationForm only requires a username and password (password1 is the initial password and password2 is the password confirmation).

To customize the pre-built form, first create a new file called forms.py in the app directory.

This new file is created in the same directory as models.py and views.py.

Then call UserCreationForm within a new class called NewUserForm and add another field called email. Save the email to the user.

Add more fields as needed to the UserCreationForm.

 

Create a register.html file

env > mysite > main > templates > main > (New File) register.html

{% extends "main/header.html" %}

{% block content %} 

{% load crispy_forms_tags %}         

<!--Register--> 
<div class="container py-5">
	<h1>Register</h1>
	<form method="POST">
		{% csrf_token %}
		{{ register_form|crispy }}                    
		<button class="btn btn-primary" type="submit">Register</button>
	</form>
	<p class="text-center">If you already have an account, <a href="/login">login</a> instead.</p>
</div>

{% endblock %}

Next, create an HTML template for the registration form. We will call this template register.html.

The form uses Bootstrap and Django crispy-forms for some added CSS.

It also extends the header.html, found in How to use the Extends and Include Django Template Tags.

 

Add a register URL to the app

env > mysite > main > urls.py

from django.urls import path
from . import views

app_name = "main"   


urlpatterns = [
    path("", views.homepage, name="homepage"),
    ...
    path("register", views.register_request, name="register")
]

Now add a register path to the app's URLs so we can refer to it in the views.

The URLs also include a homepage URL pattern found on the Django Web App Beginners Cheat Sheet.

 

Add a register function to the views

env > mysite > main > views.py

from django.shortcuts import  render, redirect
from .forms import NewUserForm
from django.contrib.auth import login
from django.contrib import messages

def register_request(request):
	if request.method == "POST":
		form = NewUserForm(request.POST)
		if form.is_valid():
			user = form.save()
			login(request, user)
			messages.success(request, "Registration successful." )
			return redirect("main:homepage")
		messages.error(request, "Unsuccessful registration. Invalid information.")
	form = NewUserForm()
	return render (request=request, template_name="main/register.html", context={"register_form":form})

Import NewUserForm from forms.py and login from django.contrib.auth. Then write a new view function called register_request.

There are two if/else statements within the function. The first checks to see if the form is being posted while the second checks to see if the form is valid. If both are true, then the form information is saved under a user, the user is logged in, and the user is redirected to the homepage showing a success message.

Else, if the form is not valid, an error message is shown. But if the request is not a POST in the first place, meaning the first if statement returned false, render the empty form in the register template. 

Please note that if you wish to add messages to your Django project you must enable the Messages Framework and import messages at the top of views.py

 

Test the register user functionality

Register form

With the register function complete, you can now go to the register URL, http://127.0.0.1:8000/register, in your browser window.

Create a testuser account.

If done correctly, you will be redirected to the homepage displaying the success message "Registration successful".

Now let's check for the user information in the Django administration.

 

Create a superuser

Terminal/Command Prompt

(env) C:\Users\Owner\Desktop\Code\env\mysite>py manage.py createsuperuser
Username (leave blank to use 'owner'): owner
Email address: 
Password: *****
Password (again): *****
Superuser created successfully.

(env) C:\Users\Owner\Desktop\Code\env\mysite>py manage.py runserver

If you haven't already, create a superuser account to gain access to the Django administration. We are going to check that the user was properly added to the database.

Run the command py manage.py createsuperuser in Windows Command Prompt and python3 manage.py createsuperuser in Mac Terminal. Then fill out the username and password.

 

Check to see if the user is listed in the Django admin

Go to the http://127.0.0.1:8000/admin/ URL where you will see a Django administration login.

If you are still logged in as the testuser you will get the warning message "You are authenticated as testuser1, but are not authorized to access this page. Would you like to login to a different account?".

Yes, you do.

Login to the admin with your superuser account then click on "Users". There you should see a list of all usernames and emails along with their staff status.

 


 

User login 

Now you may have noticed the views function we created automatically logged a user in upon account creation. Be we want the user to have the ability to login freely. So we need a login template, URL, and views function.

 

Create a login.html file

env > mysite > main > templates > main > (New File) login.html

{% extends "main/header.html" %}

{% block content %}      

{% load crispy_forms_tags %}

<!--Login--> 
<div class="container py-5">
  <h1>Login</h1>
  <form method="POST">
    {% csrf_token %}
    {{ login_form|crispy }}
    <button class="btn btn-primary" type="submit">Login</button>
  </form>
  <p class="text-center">Don't have an account? <a href="/register">Create an account</a>.</p>
</div>

{% endfor %}

The basic structure of the HTML template is similar to the register HTML. The only differences are the form and the link at the bottom.

 

Add a login URL

env > mysite > main > urls.py

from django.urls import path
from . import views

app_name = "main"   


urlpatterns = [
    path("", views.homepage, name="homepage"),
    ...
    path("register", views.register_request, name="register"),
    path("login", views.login_request, name="login")
]

Add a login path to the URLs.

 

Add a login function to the views

env > mysite > main > views.py

from django.shortcuts import  render, redirect
from .forms import NewUserForm
from django.contrib.auth import login, authenticate #add this
from django.contrib import messages
from django.contrib.auth.forms import AuthenticationForm #add this

def register_request(request):
	...

def login_request(request):
	if request.method == "POST":
		form = AuthenticationForm(request, data=request.POST)
		if form.is_valid():
			username = form.cleaned_data.get('username')
			password = form.cleaned_data.get('password')
			user = authenticate(username=username, password=password)
			if user is not None:
				login(request, user)
				messages.info(request, f"You are now logged in as {username}.")
				return redirect("main:homepage")
			else:
				messages.error(request,"Invalid username or password.")
		else:
			messages.error(request,"Invalid username or password.")
	form = AuthenticationForm()
	return render(request=request, template_name="main/login.html", context={"login_form":form})

Now go back to views.py and add authenticate to the list of imports form django.contrib.auth then import AuthenticationForm from django.contrib.auth.forms at the top of the file.

AuthenticationForm is the pre-built Django form logging in a user.

To write your login function, add an if/else statement that uses the Django function authenticate(). This function is used to verify user credentials (username and password) and return the correct User object stored in the backend.

If the backend authenticated the credentials, the function will run Django login() to log in to the authenticated user. Else if the user is not authenticated, it returns a message to the user stating they entered an invalid username or password. 

The second else statement is if the form is not valid, then it returns a similar error message. 

The final else statement is if the request is not a POST, then return the blank form in the login HTML template. 

 

Test the user login functionality

Login form

Now go to the login URL, http://127.0.0.1:8000/login, and log in to your testuser.

You will get the success message and be logged in if you entered the correct username and password.

 

BONUS: Allow signups through social media accounts (i.e. Facebook, Google, Twitter ...)

Django allauth

 

As of right now, the login is only available through Django's built-in UserCreationForm but you can easily use django-alluth to quickly handle and create signup flows. This means users can create a user account using their social media accounts. You can follow that tutorial here.

 


 

User logout

The last thing we need to handle is user logout. We will place the logout link in the navbar but it is only seen by the user if they are authenticated (i.e. they are logged in).

<!--Navbar-->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="#">Navbar</a>
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>
  <div class="collapse navbar-collapse" id="navbarText">
    <ul class="navbar-nav mr-auto">
      {% if user.is_authenticated %}
      
      <li class="nav-item">
        <a class="nav-link" href="/logout">Logout</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#">Welcome, {{user.username}}</a>
      </li>

      {% else %}

      <li class="nav-item">
        <a class="nav-link" href="/login">Login</a>
      </li>

      {% endif %}
    </ul>
  </div>
</nav>

The Django template variable {{ user }} stores the current logged-in user and their permissions are available in the template context.

All we need to do is add an if/else statement that if the user is authenticated show the logout link and their username, else show a login link.

This is a basic navigation bar from the Bootstrap documentation. If you are looking to further customize it, then refer to the article 10 Custom Bootstrap Navbars.

 

Add a logout URL

env > mysite > main > urls.py

from django.urls import path
from . import views

app_name = "main"   


urlpatterns = [
    path("", views.homepage, name="homepage"),
    ...
    path("register", views.register_request, name="register"),
    path("login", views.login_request, name="login"),
    path("logout", views.logout_request, name= "logout"),
]

Now add a logout URL path to the app's URLs.

 

Add a log out function

env > mysite > main > views.py

from django.shortcuts import  render, redirect
from .forms import NewUserForm
from django.contrib.auth import login, authenticate, logout #add this
from django.contrib import messages
from django.contrib.auth.forms import AuthenticationForm

def register_request(request):
	...

def login_request(request):
	...

def logout_request(request):
	logout(request)
	messages.info(request, "You have successfully logged out.") 
	return redirect("main:homepage")

Finally, add a logout function to the views file. The logout_request function uses the Django function logout() to log the user out of their account and redirect them to the homepage when the logout URL is requested. 

 

Test the user logout functionality

User logged in

Go to the browser window and reload the page.

Login back in if you need to.

There should be a navbar with a logout link next to the welcome user text.

 

Logout

 

Click the logout link and the homepage should reload showing the logout message while only displaying a login link.

 

What happens if the user forgets their password?

The last thing to think about are user password resets.

Django also handles this functionality using its authentication system.

If you wish to learn how to allow users the ability to send a reset password to themselves, follow the tutorial Reset User Passwords in Django.

Password reset form

 


 

Django + Bootstrap Login Templates

If you are looking to further customize these register and login forms, checkout 14 Custom User Login Forms

User login forms

 


 

Solving Possible Errors

Page not found (404)/ Not Found

If you get the error Page not found (404) when going to http://127.0.0.1:8000/register, check to make sure register is added the urls.py

env > mysite > main > urls.py

from django.urls import path
from . import views

app_name = "main"   


urlpatterns = [
    path("", views.homepage, name="homepage"),
    ...
    path("register", views.register_request, name="register"),
]

Go to urls.py and make sure a register path, connected to a view function, is added. 

 

Page not found (404)/ Not Found is still occurring

If you create the register path with a trailing slash then decide to delete the slash, your browser will continue to look for the cached path with the trailing slash.

To fix this, you have two options:

(1) Change the URL path to match the one requested

The Django yellow page states the current path requested.

The path requested

"The current path 'register/' did match any of theses (the URLs listed above)'. 

 

env > mysite > main > urls.py

from django.urls import path
from . import views

app_name = "main"   


urlpatterns = [
    path("", views.homepage, name="homepage"),
    ...
    path("register/", views.register_request, name="register"),
]

Go to urls.py and add the exact path stated in the error, in this case, the register path with a trailing slash.

Save the file, and the error will no longer occur. 

 

(2) Clear your browser cache

The second option is to go to your browser history and delete the cached data. 

 

TemplatSyntaxError at /register

If you get a template syntax error stating 'crispy_forms_tags' is not registered tag library, you need to make sure django-crispy-forms is installed properly. 






Post a Comment
Join the community

16 Comments
Ivan Nov. 24, 2020, 6:14 a.m.

Hi! I have a problem in my code: Error: "Not Found: /register/" Can You help me to solve this?

Jaysha replying to Ivan Nov. 24, 2020, 11:48 a.m.

Your problem is most likely from the path 'register/' cached in your browser. Clear your browser cache or change the URL path back to 'register/' to solve this error. The article is now updated to help others solve this same error.

Plachey1 March 23, 2021, 5:07 a.m.

Hello. Where get path("", views.homepage, name="homepage") this vews views.homepage and main/header.html ? Thanks

Jaysha replying to Plachey1 March 23, 2021, 10:35 a.m.

Thanks for your feedback. I updated the article to link to tutorials on creating a homepage and using a header.html. There is also an all-in-one guide at https://www.ordinarycoders.com/blog/article/django-beginners-guide.

Plachey1 March 23, 2021, 5:33 a.m.

could use attach github link

Mwambu April 7, 2021, 7:53 a.m.

Great tutorial, Jaysha. This works like a charm. Kudos and keep up the good work sir

Jaysha replying to Mwambu April 7, 2021, 10:49 a.m.

Thanks!

Hemanth May 17, 2021, 7:08 a.m.

Can I store the registration details in database? If yes How?

Jaysha replying to Hemanth May 19, 2021, 10:59 a.m.

Hello Hemanth, the registration details are saved to sqlite3 in the register_requst function. Login to your Django admin and click "Users" to see the list of user accounts.

Muhammad7 May 18, 2021, 12:53 a.m.

Thanks for amazing tutorials Jaysha!

Vampire_ May 28, 2021, 7:18 a.m.

what is header.html in your code please clarify this

Jaysha replying to Vampire_ May 28, 2021, 9:49 a.m.

The "Create a register.html file" section of this article includes a link on creating and extending the header.html.

Priya July 2, 2021, 10:54 p.m.

hello! I have a problem in my code: when I hit register it takes me back to homepage but when I check admin the user is not created there. Can you help me with this?

Jaysha replying to Priya July 5, 2021, 4:08 p.m.

Double-check your register_request view function is correct, including indentations. The homepage return redirect needs to be in the form validation if condition.

Jaysha replying to Jaysha July 5, 2021, 4:11 p.m.

You can also replace the messages error in the view function with messages.error(request, form.errors). That way you can see the exact reason your form isn't valid. Do you have a Django yellow page or error in the terminal you can share?

Farhan Aug. 1, 2021, 4:26 a.m.

I am having an issue with this approach. When I try to signup a user it only allows one user after that I am unable to register/signup a user.

8
Jaysha
Written By
Jaysha
Hello! I enjoy learning about new CSS frameworks, animation libraries, and SEO.