Get Started With Django Part 2: Django User Management

Get Started With Django User Management

by Paweł Fertyk Dec 18, 2024 intermediate django

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Building a Django User Management System

Django user management allows you to integrate user authentication and management into your web applications. By using Django, you can leverage the framework’s built-in authentication system to manage user registration, login, and logout. With just a few additional templates, you can enable users to reset and change their passwords independently.

This tutorial guides you through setting up a basic user management system with Django that you can extend later. You’ll learn how to create a dashboard, implement user registration, and connect authentication URLs, as well as customize templates for login, logout, and password management.

By the end of this tutorial, you’ll understand that:

  • Django’s user authentication is a built-in authentication system that comes with pre-configured URLs and views.
  • Authentication verifies user identity, while authorization determines user permissions within Django.
  • Registering as a user in Django requires setting up views, templates, and URLs.
  • Creating a login system in Django involves built-in authentication views and creating custom templates.
  • Resetting passwords in Django involves configuring email backends for sending reset links.

This tutorial focuses on user authentication and user management. If you want to learn more about permissions and groups, then you can check out the tutorial about managing users in Django’s admin site.

Start With the Basics

For bigger projects, you may consider creating a custom user model. In this tutorial, you’ll be using Django’s built-in user model. This is a great place to start to familiarize yourself with user authentication in general.

In this section of the tutorial, you’ll first create a small Django project with a users app. Then, you’ll make some adjustments to Django’s password validator to make your development more convenient. Finally, you’ll create an admin user to verify your setup.

Set Up the Django Project

It’s a good idea to use a virtual environment when working with Python projects. That way, you can always be sure that the python command points to the right version of Python and that the modules required by your project have the correct versions. To read more about creating virtual environments, check out Python Virtual Environments: A Primer.

Select your operating system below and use your platform-specific command to set up a virtual environment:

Windows PowerShell
PS> python -m venv venv\
PS> .\venv\Scripts\activate
(venv) PS>
Shell
$ python -m venv venv/
$ source venv/bin/activate
(venv) $

With the above commands, you create and activate a virtual environment named venv by using Python’s built-in venv module. The parenthesized (venv) in front of the prompt indicate that you’ve successfully activated the virtual environment.

Now that the environment is ready, you can install Django, start a new project, and create an application to store all your user management code:

Shell
(venv) $ python -m pip install Django
(venv) $ django-admin startproject user_auth_intro
(venv) $ cd user_auth_intro
(venv) $ python manage.py startapp users

In this example, you name your project user_auth_intro and your application users. To include the users app in your Django project, you need to add a reference to the app’s configuration class at the beginning of the INSTALLED_APPS list in settings.py:

Python user_auth_intro/settings.py
 1# ...
 2
 3INSTALLED_APPS = [
 4    "users.apps.UsersConfig",
 5    "django.contrib.admin",
 6    "django.contrib.auth",
 7    "django.contrib.contenttypes",
 8    "django.contrib.sessions",
 9    "django.contrib.messages",
10    "django.contrib.staticfiles",
11]
12
13# ...

By adding users.apps.UsersConfig, you let Django know that the users app you just created exists. If you have a look at the INSTALLED_APPS list, then you’ll spot Django’s default authentication system on line 6. In django.contrib.auth, Django stores the core of its authentication framework and the default models that you’ll build on later.

Next, apply the migrations and run the Django development server:

Shell
(venv) $ python manage.py migrate
(venv) $ python manage.py runserver

These commands create all default models in the database and start the Django development server.

Deactivate the Password Validator

By default, Django enforces strong passwords to make user accounts less prone to attacks. Since you’ll need to change passwords often throughout this tutorial, figuring out a strong password each time would be inconvenient.

To address this issue, you can disable password validators in the settings. Just comment them out, leaving an empty list:

Python user_auth_intro/settings.py
# ...

AUTH_PASSWORD_VALIDATORS = [
    # {
    #     "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
    # },
    # {
    #     "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
    # },
    # {
    #     "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
    # },
    # {
    #     "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
    # },
]

# ...

Now Django will allow you to set passwords like pass or even 1234, making your work with the user management system much quicker. Just remember to enable the validators in your actual application!

Create a Django Admin User

For this tutorial, it would also be useful to have access to the admin site so you can track newly created users and their passwords. Go ahead and create an admin user:

Shell
(venv) $ python manage.py createsuperuser
Username: admin
Email address: admin@example.com
Password: 1234
Password (again): 1234
Superuser created successfully.

Adjusting Django’s password validators like this makes exploring the authentication system more convenient. For example, you can use any password you like for your admin user. As mentioned earlier, you’ll need to remember to enable the password validators later.

If you want, you can head over to http://127.0.0.1:8000/admin/ to verify that you can log in to Django’s admin site. When doing so, pay attention to the forms and the buttons you encounter during the login process. You’ll re-create a similar flow in your users application.

Set Up the Django User Management System

Most user management systems have some sort of main page, usually referred to as the dashboard. On this dashboard, you’ll find the links to sign up, log in, or log out as a user.

In addition to the dashboard, you’ll have more pages later. That’s why you’ll start by creating a base template from which the other pages can inherit a consistent structure with their child templates.

Create the Base Template Structure

You’ll place all the templates you use in this tutorial in the users/templates/ directory. The users/templates/ directory doesn’t exist by default, so you’ll have to create it first. Then, create base.html with the following content:

HTML users/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>User Management Intro</title>
</head>
<body>
    <h1>Welcome!</h1>
    {% block content %}
    {% endblock content %}
</body>
</html>

The base template contains the basic structure of a typical HTML document. The headline shows a Welcome! message, and you define a block named content. The block is empty for now, but other templates are going to use it to include their own content.

To organize your templates properly so Django can find them, create two folders for your child templates:

  1. users/templates/users/
  2. users/templates/registration/

This template folder naming scheme organizes your templates neatly. Nesting template folders like this may look a bit redundant, but it allows you to create templates with the same names in the subfolders. In this tutorial you’ll only work with one base template file, so it’s okay to keep base.html directly in the templates/ folder.

Add a Dashboard Page

When you create new pages in Django, you typically need a template, a view that renders the template, and a URL that uses the view. Your dashboard page follows this three-step build process as well. Since you haven’t defined any URLs for the users app yet, there’s also a fourth step to connect the URLs from users to your Django project. You’ll tackle these steps one by one!

Start by creating the template for the dashboard. As this template is specific for the users/ app, you save it in the users/template/users/ folder. The name of the template should be dashboard.html, and it should extend the base template:

HTML users/templates/users/dashboard.html
{% extends "base.html" %}
{% block content %}
    Hello, {{ user.username|default:"Guest" }}!
{% endblock content %}

For now, the template only shows the welcome message with the current user’s username. If the user isn’t logged in, then Django will still set the user variable using an AnonymousUser object. An anonymous user always has an empty username, so the dashboard will show Hello, Guest!

Next, create a dashboard view in users/views.py that renders the dashboard.html template:

Python users/views.py
from django.shortcuts import render

def dashboard(request):
    return render(request, "users/dashboard.html")

One interesting detail about the dashboard view is that you’re rendering the template just by providing the request object. Still, Django will be able to work with the user variable you use in dashboard.html because the user information is stored in RequestContext. You’ll verify this behavior in a moment.

For now, continue by creating a users/urls.py file and add a path for the dashboard view:

Python users/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path("dashboard/", views.dashboard, name="dashboard"),
]

Finally, add your application’s URLs to your project’s URLs:

Python user_auth_intro/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("", include("users.urls")),
    path("admin/", admin.site.urls),
]

Now you can test the dashboard view. Open http://127.0.0.1:8000/dashboard/ in your browser. You should see a screen similar to this one:

Dashboard for guests

This means that Django successfully worked with the user template variable. In this case, the user is an anonymous user, which doesn’t have a .username property, so Hello, Guest! is shown.

If you’re not logged in, then the dashboard page greets you as a guest. When you open the admin site at http://127.0.0.1:8000/admin/ and log in as the admin user, your dashboard should now look a bit different when you go back to it:

Dashboard for logged users

As you can see, your new template correctly displays the name of the currently logged-in user. In this case, the dashboard greets you as an admin. That’s a great start!

Implement User Registration

Currently, you’re the admin of the project, and you can log in through the login form on the admin site. If you want to add new users to your project, then you need to create them yourself. That’s not very practical. Instead, you’ll build a registration page where users can sign up on their own.

To create this page, you’ll follow the same steps as for any other page in a Django project. Begin by registering a URL that connects to a view, which in turn renders a template. Start by adding a “sign_up” URL to the urls.py file in your users app:

Python users/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path("dashboard/", views.dashboard, name="dashboard"),
    path("sign_up/", views.sign_up, name="sign_up"),
]

Next, add a sign_up view that adds a user to the database when you send the registration form with a "POST" request:

Python users/views.py
 1from django.contrib.auth import login
 2from django.contrib.auth.forms import UserCreationForm
 3from django.shortcuts import redirect, render
 4from django.urls import reverse
 5
 6def dashboard(request):
 7    return render(request, "users/dashboard.html")
 8
 9def sign_up(request):
10    if request.method == "POST":
11        form = UserCreationForm(request.POST)
12        if form.is_valid():
13            user = form.save()
14            login(request, user)
15            return redirect(reverse("dashboard"))
16    else:
17        form = UserCreationForm()
18    return render(request, "registration/sign_up.html", {"form": form})

The view is very typical for a view that works with forms. When you access the sign_up page with a "GET" request, you’ll see an empty form that you can fill out to sign up. After you send a valid form with the request, you save the form data to your database. In this case, it creates a new user. Then, you redirect the request to the dashboard page.

There’s one important detail to note after saving the form. In line 14, you use Django’s login() function to additionally log the user in. If you removed this line, then you’d register the user but the user would stay logged out after signing up. Using login() saves your users a step and streamlines the registration process.

In line 18, you’re serving the sign_up.html template from within a registration/ subfolder. Go ahead and create sign_up.html with this content:

HTML users/templates/registration/sign_up.html
{% extends "base.html" %}
{% block content %}
    <h2>Sign Up</h2>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="Sign Up" />
    </form>
    <p>
        <a href="{% url 'dashboard' %}">Back to dashboard</a>
    </p>
{% endblock content %}

This will display a Sign Up heading, followed by a login form. Django uses a dictionary, also known as a context, to pass data to a template while rendering it. In this case, a variable called form will already be included in the context—all you need to do is display it. Using {{ form.as_p }} will render the form as a series of HTML paragraphs.

The {% csrf_token %} line inserts a Cross-Site Request Forgery (CSRF) token, which is required by every Django form. There’s also a button for submitting the form and, at the end of the template, a link that will take your users back to the dashboard.

To check if your sign-up page works, head over to http://127.0.0.1:8000/sign_up and create a new user:

After sending the registration form, the user logs in automatically and is greeted personally on the dashboard page. Congratulations—you can now register new users! Next, you’ll add functionality for logging in and out.

Work With Django Auth

A complete website needs a bit more than just a dashboard to handle user management. Luckily, Django comes with an authentication system and an authorization system, which are commonly shortened as Django auth to cover both terms:

  • Authentication: Is the verification process to identify a user, for example, a login.
  • Authorization: Is the process of determining what a user is allowed to do, for example, after logging in.

Django auth has a lot of user management-related URLs and views that’ll take care of the most important processes, including login, logout, password change, and password reset. However, you’ll need to create the templates to accompany these processes.

In this section of the tutorial, you’ll first add the URLs for logging in and logging out to your project. Then, you’ll take care of all the templates to make the authorization process for your users smooth.

Connect the Auth URLs and Views

To make use of Django’s built-in authentication and authorization systems, you need to work with django.contrib.auth. The entryway is the module’s URLConf, which is short for URL configuration. The first step is to add django.contrib.auth.urls to your application’s urls.py:

Python users/urls.py
from django.urls import include, path
from . import views

urlpatterns = [
    path("accounts/", include("django.contrib.auth.urls")),
    path("dashboard/", views.dashboard, name="dashboard"),
    path("sign_up/", views.sign_up, name="sign_up"),
]

By including Django’s django.contrib.auth.urls URLConf to your user application’s URL, you automatically add a bunch of URLs to your project, such as URLs for logging users in and out. Each URL comes with a name that you can later use in your templates, and the views that are connected to the URLs enable you to perform an authentication task:

URL Task Name
accounts/login/ Log a user into your application "login"
accounts/logout/ Log a user out of your application "logout"

Additionally, django.contrib.auth.urls provides URLs for more advanced tasks, such as changing and resetting passwords. You’ll explore these later in this tutorial.

Now that the accounts/ URLs exist in your app and they’re already connected to corresponding views in django.contrib.auth.views, take the plunge and visit http://127.0.0.1:8000/accounts/login/:

Django template error message

The TemplateDoesNotExist error is expected because you need to create the templates yourself. But like other Python exceptions, the error message is actually quite helpful and gives you two important bits of information:

  1. The "login" URL finds the LoginView.
  2. The LoginView looks for a template in registration/ named login.html.

You can visit the other URLs, and you’ll see similar error messages for most of them. To get rid of the errors, you must create the templates that the views expect.

If you scroll down the page with the error message, then you can have a look at the template-loader postmortem section. There, you can see that Django particularly tries to load templates from the path users/templates/registration/ first. This is where you’ll create your user management templates.

Create a Login Page

For the login page, Django will try to use a template called login.html that lives inside users/templates/registration/. Inside registration/, add a login.html file with this content:

HTML users/templates/registration/login.html
{% extends "base.html" %}
{% block content %}
    <h2>Login</h2>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="Login" />
    </form>
    <p>
        <a href="{% url 'dashboard' %}">Back to dashboard</a>
    </p>
{% endblock content %}

The template for your login page looks very similar to your sign up template. The only difference is the headline and the value of the button. Also, the form will only show one password entry field compared to the sign up form where you have to repeat the password you set.

When you visit http://127.0.0.1:8000/accounts/login/ again, then you can see that Django has now successfully rendered your login page with the new template:

Login page

Use your admin user credentials and select Login. Don’t be alarmed if you see an error screen again. Just like before, the error message gives you helpful information about what went wrong:

Missing user profile url in Django

According to the error message, Django can’t find a path for accounts/profile/, which is the default destination for your users after a successful login. Apparently, Django doesn’t come equipped with a profile view. But that’s not a problem because you already have the dashboard view that you can reuse instead.

Redirect Logins

Django gives you two options for changing the default redirection after a successful login. The first option is to set the LOGIN_REDIRECT_URL in your settings.py file to the preferred URL your request should be redirected to. The second option is to adjust the form of your login.html template.

Generally, adjusting LOGIN_REDIRECT_URL is a valid strategy. But since you want to redirect to your dashboard page, you’d reference the URL from another app in your project’s settings. If you ever decide to remove your users app from your Django project sometime in the future, then you’ll also need to remember to remove or update LOGIN_REDIRECT_URL.

Adjusting the form of login.html gives you granular control about where you want to redirect the request after a successful login. All you need to do is adjust the next parameter that you send with the form.

Open login.html and add the next parameter with the help of a hidden input field:

HTML users/templates/registration/login.html
{% extends "base.html" %}
{% block content %}
    <h2>Login</h2>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="Login" />
        <input type="hidden" name="next" value="{% url 'dashboard' %}" />
    </form>
    <p>
        <a href="{% url 'dashboard' %}">Back to dashboard</a>
    </p>
{% endblock content %}

Now try to log in again. You’ll be redirected to the dashboard page where you’re greeted with your username.

Enable Logging Out

Now your users can log in, but they should also be able to log out. This process is a bit different from logging in. If you have a look at the URLs that Django auth provides, you’ll spot an accounts/logout/ URL. However, you’re not supposed to visit this URL in the browser like you did with the the login page.

Instead of creating a template for the logout view, you must create a form that sends a post request to the accounts/logout/. Then, you can include this form anywhere you want to provide the logout functionality.

To indicate that the logout template is supposed to be included by other templates, you can prefix the filename with an underscore _ and name it _logout.html. Store the template next to login.html and follow a similar pattern with the hidden field:

HTML users/templates/registration/_logout.html
 1<form method="post" action="{% url 'logout' %}">
 2    {% csrf_token %}
 3    {{ form.as_p }}
 4    <input type="submit" value="Logout" />
 5    <input type="hidden" name="next" value="{% url 'dashboard' %}" />
 6</form>

Since the template is supposed to be included by other templates and isn’t to be rendered independently, you don’t need to extend any other templates. Instead, you just add the code for the form.

In line 1, you’re also adding the action attribute to tell the browser where the request should be sent to. It’s important to add the "logout" page here. Otherwise, the form would send the POST request to the same URL you’re on right now and not log you out.

Open dashboard.html and include the _logout.html form:

HTML users/templates/users/dashboard.html
{% extends "base.html" %}
{% block content %}
    Hello, {{ user.username|default:"Guest" }}!
    <hr />
    {% include "registration/_logout.html" %}
{% endblock content %}

When you visit http://127.0.0.1:8000/dashboard/, you’ll see the button that logs your user out:

Django dashboard with logout button

Currently, the logout process is very subtle. When you click the button, then you see your username switch to “Guest” in the dashboard’s greeting message. But the logout button is still displayed even after you’ve logged out. So before wrapping up this section, it’s time to update the dashboard to reflect the current state of your user management system.

Update the Dashboard

Your dashboard is quite basic at the moment. The logout button is present at all times, even when users are already logged out. If you want to sign up or log in, then you must visit the URLs by typing them into the browser’s address bar. You can fix these inconveniences by updating the dashboard template:

HTML users/templates/users/dashboard.html
{% extends "base.html" %}
{% block content %}
    Hello, {{ user.username|default:"Guest" }}!
    <hr />
    {% if user.is_authenticated %}
        {% include "registration/_logout.html" %}
    {% else %}
        <a href="{% url 'login' %}">Login</a>
        <a href="{% url 'sign_up' %}">Sign up</a>
    {% endif %}
{% endblock content %}

To conditionally show the logout button or the login and signup links, you’re making use of user.is_authenticated. If the user is authenticated, you show the logout button. Otherwise, you render the links to your login page and the sign-up page.

You can visit http://127.0.0.1:8000/dashboard/ and see how the updated dashboard works:

When a user is logged out, they have the option to log in or sign up. If they already have an account, then they can click Login and enter their credentials on the login page.

For users who don’t have an account, you provide the option for them to sign up. In both situations, the user is logged in afterward, which is when your dashboard shows the logout button.

That’s perfect! It looks like you now have a functioning user management system. As long as the users remember their passwords, everything will be fine.

Handle Passwords

Currently, your user management system is missing two important features for handling passwords:

  1. Changing Your Password: You want to be able to change your password now and then to keep your account secure.
  2. Resetting Your Password: In case you forget your password, you want to be able to reset it.

Building upon Django auth for your user management gives you a head start in implementing functionality for changing and resetting passwords in your project.

Remember how when you included Django’s django.contrib.auth.urls URLConf to your user application’s URL, you automatically added a bunch of URLs to your project? For example, it added URLs for logging users in and out. Well, it also added these URLs for changing and resetting passwords:

URL Task Name
accounts/password_change/ Change a password "password_change"
accounts/password_change/done/ Show a confirmation that a password was changed "password_change_done"
accounts/password_reset/ Request an email with a password reset link "password_reset"
accounts/password_reset/done/ Show a confirmation that a password reset email was sent "password_reset_done"
accounts/reset/<uidb64>/<token>/ Set a new password using a password reset link "password_reset_confirm"
accounts/reset/done/ Show a confirmation that a password was reset "password_reset_complete"

Just like before, each URL comes with a name that you can later use in your templates, and the views that are connected to the URL enable you to perform an authentication task. Let’s start with changing passwords!

Change Passwords

At some point, your users might want to change their passwords. Instead of making them ask the admin to do it for them, you can add a password change form to your application. Django needs two templates to make this work:

  1. registration/password_change_form.html to display the password change form.
  2. registration/password_change_done.html to show a confirmation that the password was successfully changed.

Start with registration/password_change_form.html:

HTML users/templates/registration/password_change_form.html
{% extends "base.html" %}
{% block content %}
    <h2>Change password</h2>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="Change" />
    </form>
    <a href="{% url 'dashboard' %}">Back to dashboard</a>
{% endblock content %}

This template looks almost the same as the login template you created earlier. But this time, Django will put a password change form here, not a login form, so the browser will display it differently.

The other template you need to create is registration/password_change_done.html:

HTML users/templates/registration/password_change_done.html
{% extends "base.html" %}
{% block content %}
    <h2>Password changed</h2>
    <a href="{% url 'dashboard' %}">Back to dashboard</a>
{% endblock content %}

After changing the password, the user will see the "password_change_done" page to verify that the password change was successful. Just like with other pages, it’s a good idea to add a link to the dashboard.

To make the password change conveniently accessible, add a link on the dashboard to your newly created password change form. You just have to make sure that it’s only shown to users who are logged in:

HTML users/templates/users/dashboard.html
{% extends "base.html" %}
{% block content %}
    Hello, {{ user.username|default:"Guest" }}!
    <hr />
    {% if user.is_authenticated %}
        {% include "registration/_logout.html" %}
        <a href="{% url 'password_change' %}">Change password</a>
    {% else %}
        <a href="{% url 'login' %}">Login</a>
        <a href="{% url 'sign_up' %}">Sign up</a>
    {% endif %}
{% endblock content %}

If you follow the link in your browser, then you should see the following form:

Change password form

Go ahead and test it. Change the password, log out, and log in again. You can also try to access the password change page without logging in by accessing the URL http://127.0.0.1:8000/accounts/password_change/ directly in your browser. Django is clever enough to detect that you should log in first and will automatically redirect you to the login page.

Mistakes happen to the best of us, and every now and then, someone might forget a password. Your Django user management system should handle that situation, too. This functionality is a bit more complicated because, in order to work with password reset links, there are three requirements:

  1. Your users must provide an email address when they sign up.
  2. You must have an email template to send.
  3. Your application must be able to send emails.

So far, a user only needs a username and a password to sign up for your application. That’s a good start. However, when they want to reset their password, just asking them for their username is risky. Instead, you’ll want to send an email to the email address they provide. This approach ensures that users can reset passwords themselves.

To request an email address during registration, you’ll need to adjust the registration form. To do so, create a new Python file called users/forms.py and add a custom form:

Python users/forms.py
from django.contrib.auth.forms import UserCreationForm

class CustomUserCreationForm(UserCreationForm):
    class Meta(UserCreationForm.Meta):
        fields = UserCreationForm.Meta.fields + ("email",)

In your newly created forms.py file, you’re creating a CustomUserCreationForm that extends Django’s UserCreationForm. The inner class Meta keeps additional information about the form and, in this case, extends UserCreationForm.Meta, so almost everything from Django’s form will be reused.

The key difference is the .fields attribute, which determines the fields that will be included in the form. Your custom form will use all the fields from UserCreationForm and will add the email field.

Now that the form is ready, update the sign_up view in users/views.py to work with CustomUserCreationForm:

Python users/views.py
from django.contrib.auth import login
# Remove: from django.contrib.auth.forms import UserCreationForm
from django.shortcuts import redirect, render
from django.urls import reverse

from .forms import CustomUserCreationForm

def dashboard(request):
    return render(request, "users/dashboard.html")

def sign_up(request):
    if request.method == "POST":
        form = CustomUserCreationForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            return redirect(reverse("dashboard"))
    else:
        form = CustomUserCreationForm()
    return render(request, "registration/sign_up.html", {"form": form})

Instead of using UserCreationForm, you’re now working with CustomUserCreationForm that you import from forms.py. Inside sign_up(), you need to update the values of form to use CustomUserCreationForm. The rest of the sign_up stays the same.

Create a new user to check out the updated registration process:

User registration form

Perfect. Now that you require email addresses for new users, you can continue and create a template named password_reset_form.html in registration/:

HTML users/templates/registration/password_reset_form.html
{% extends "base.html" %}
{% block content %}
    <h2>Send password reset link</h2>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="Send Reset Link" />
    </form>
    <p>
        <a href="{% url 'dashboard' %}">Back to dashboard</a>
    </p>
{% endblock content %}

This template will be served when you visit http://127.0.0.1:8000/accounts/password_reset/. It’s also a good idea to link to this page on your login page:

HTML users/templates/registration/login.html
{% extends "base.html" %}
{% block content %}
    <h2>Login</h2>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="Login" />
        <input type="hidden" name="next" value="{% url 'dashboard' %}" />
    </form>
    <p>
        <a href="{% url 'password_reset' %}">Forgot your password?</a>
        <a href="{% url 'dashboard' %}">Back to dashboard</a>
    </p>
{% endblock content %}

Now when the user’s on the login page and forgets the password, they can click the link to the "password_reset" page. After they request a reset link, Django sends a reset link to the provided email address and redirects the user to the "password_reset_done" page. For this page, you also need to create a template:

HTML users/templates/registration/password_reset_done.html
{% extends "base.html" %}
{% block content %}
    <h2>Password reset link sent</h2>
    <a href="{% url 'dashboard' %}">Back to dashboard</a>
{% endblock content %}

The "password_reset_done" name is a bit misleading. At this point, Django hasn’t reset the password yet. The password reset will only take place once the user clicks the link in the password reset email that Django sends.

Send Password Reset Emails

Go ahead and try out requesting the password reset. In the browser, everything seems to work as expected. But Django actually doesn’t send any emails at this point. To do so, you’ll have to set up an email backend for your project.

There are different kinds of email backends in Django. During development, it’s often most convenient to just output any emails to your terminal by using the console email backend. Open settings.py and add this line of code to the file:

Python settings.py
# ...

EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"

# ...

In production, you’ll probably want to define a custom email backend. For now, setting EMAIL_BACKEND to "django.core.mail.backends.console.EmailBackend" will allow you to copy and paste the password reset link from the terminal.

Try out requesting the password reset again. While doing so, have a look at the output in the terminal window where your Django development server is running. After clicking the Send Reset Link button, you’ll see the content of the reset email in the output of your terminal:

Shell
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
Subject: Password reset on 127.0.0.1:8000
From: webmaster@localhost
To: philipp@realpython.com
Date: Wed, 18 Dec 2024 10:08:34 -0000
Message-ID: <173287491425.58492.16858044530563413153@philipp>

You're receiving this email because you requested a password reset
for your user account at 127.0.0.1:8000.

Please go to the following page and choose a new password:

http://127.0.0.1:8000/accounts/reset/Nw/ch906a-de23fda8df503bf8567b6485a/

Your username, in case you've forgotten: Philipp

Thanks for using our site!

The 127.0.0.1:8000 team

Some of the content may look a bit weird because you’re on your local development server. The important part of this output is the reset link. That’s the link the user must click to trigger the password reset.

Reset Passwords

Before the link can be used, you need to create two other templates that’ll be displayed during the password reset process:

  1. registration/password_reset_confirm.html
  2. registration/password_reset_complete.html

Start with password_reset_confirm.html, which will display a password reset form:

HTML users/templates/registration/password_reset_confirm.html
{% extends "base.html" %}
{% block content %}
    <h2>Confirm password reset</h2>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="Confirm" />
    </form>
    <a href="{% url 'dashboard' %}">Back to dashboard</a>
{% endblock content %}

After sending this form, Django will redirect you to a success page. The template for this page is password_reset_complete.html:

HTML users/templates/registration/password_reset_complete.html
{% extends "base.html" %}
{% block content %}
    <h2>Password reset completed</h2>
    <a href="{% url 'login' %}">Login</a>
{% endblock content %}

Now that you have all templates present, copy the password reset link from the email and paste it into your browser. The link should have a pattern like accounts/reset/<uidb64>/<token>/. The page you’ll see after visiting the page reset link contains your password reset form:

Password reset confirmation

Once you enter a new password and click Confirm, you’re redirected to the "password_reset_complete" page. Afterward, you can log in with your new password.

Reactivate the Password Validator

At the beginning of this tutorial, you disabled the password validators in your project’s settings to make your development workflow more convenient. Now that your user management system is complete, it’s a good time to reactivate the password validators again:

Python user_auth_intro/settings.py
# ...

AUTH_PASSWORD_VALIDATORS = [
    {
        "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
    },
]

# ...

Now Django will enforce strong passwords for new users. You can have a look at the rules when you visit http://127.0.0.1:8000/accounts/password_change/ with a logged-in user:

Django password rules

In addition to other rules, new passwords must now contain at least eight characters and can’t be entirely numeric. Now you not only have a working user management system but one that ensures secure passwords for your users.

Conclusion

Congratulations! You’ve added a user management system to your Django web app by using Django’s built-in authentication system. To improve the user experience, you can now extend some of Django’s base functionality to your liking.

In this tutorial, you learned how to:

  • Leverage Django’s user authentication implementation
  • Make development more convenient by adjusting Django’s password validators
  • Create an application where users can register, log in, and log out
  • Provide users with tools to reset and change passwords on their own
  • Edit the default Django templates responsible for user management
  • Work with password reset links

Now you’re all set to invite users to your web application. If you want to dive even deeper into user management in Django, then you can check out the tutorial about managing users in Django’s admin site.

Frequently Asked Questions

Now that you have some experience with Django user management in Python, you can use the questions and answers below to check your understanding and recap what you’ve learned.

These FAQs are related to the most important concepts you’ve covered in this tutorial. Click the Show/Hide toggle beside each question to reveal the answer.

You set up user authentication in Django by utilizing Django’s built-in authentication system, which includes URLs and views. Then you need to create the templates for login, logout, and password management.

Authentication verifies the identity of a user, like logging in, while authorization determines what actions a user is allowed to perform once authenticated.

No. Since Django version 5, you must log users out by sending a POST request to the "logout" view.

You create a login system by including path("accounts/", include("django.contrib.auth.urls")) to the urlpatterns list in your urls.py file. Then you must create a login.html file in a registration/ template folder.

You reset a password by using Django’s password reset views and setting up templates, sending a reset link to the user’s email, and allowing the user to set a new password via the link.

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Building a Django User Management System

🐍 Python Tricks 💌

Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

About Paweł Fertyk

I'm a software developer with a passion for Python and open source.

» More about Paweł

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Master Real-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

Master Real-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. Get tips for asking good questions and get answers to common questions in our support portal.


Looking for a real-time conversation? Visit the Real Python Community Chat or join the next “Office Hours” Live Q&A Session. Happy Pythoning!