How I Added a User Authentication to My Django Real Estate Application

We created a real estate application using the Django web framework. In the project, we populated our database with information about properties available for sale. We also created a form to quickly add more listings if we don’t want to use the admin panel.

In this tutorial, we will add a registration, login, and logout functionality to the real estate application so that only registered individuals can make inquiries. You will benefit greatly if you go through the previous project first before attempting this one.

User Registration

The advantage of using Django over other web frameworks is that certain features are already inbuilt. User authentication is not an exception. No need to reinvent the wheel. All we have to do is to select and configure it to our needs. To create the registration form, we will import the UserCreationForm class in our forms.py file.

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


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

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

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

We create a RegisterForm class that inherits from the UserCreationForm, and add a required field, EmailField to the form. The Meta class is used to change the behavior of a model. In it, we specify that the User model will be used plus the fields we want to be displayed on the form. The second password field is for confirmation.

The save() method is used to override the default behavior of the UserCreationForm class. It first uses the super() method to call the parent class’ save method with commit=False. This creates a new user object without saving it to the database because it hasn’t received the user’s email attribute. Once received, the user object can then be saved to the database.

But you may wonder, what if we do not use the save() method?

Of course, it will use the parent’s class save method. However, since the email field is not included in the default field of the UserCreationForm class, the save() method ensures that the email is saved along with the other data.

The views for this form will be:

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

def register(request):
    if request.method == 'POST':
        form = RegisterForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, 'Your account has been created successfully!')
            return redirect(‘login’)
    else:
        form = RegisterForm()
    return render(request, 'register.html', {'form': form})

If the request method is POST, the form has been submitted. If the form is valid, it creates a new user, displays a success message and redirects to the login page. Otherwise, it creates a new instance of the RegisterForm and passes the form as context in the register.html template.

The URL route will be like this:

path('register', register, name='register'),

For the templates:

{% extends "base.html" %}
{% block content %}
{% load tailwind_filters %}

<!--Register-->
<div class="container py-5">
        <h1>Register</h1>
        <form method="POST">
                {% csrf_token %}
                {{ form|crispy }}
                <button class="btn btn-primary" type="submit">Register</button>
        </form>
        {% if messages %}
    <ul class="messages">
            {% for message in messages %}
            <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
            {% endfor %}
    </ul>
        {% endif %}

        <p class="text-center">If you already have an account, <a href="{% url 'login' %}">login</a> instead.</p>
</div>

{% endblock %}

The form is loaded using crispy tailwind. You can check the source code for more details. If there are messages stored in the message variable, it will also be displayed automatically.

User Login

Let us create the login page where users will be redirected after a successful registration.

from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login, authenticate

def login_view(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("contact")
            else:
                messages.error(request,"Invalid username or password.")
    form = AuthenticationForm()
    return render(request, 'login.html', {"form": form})

We import the AuthenticationForm class and create an instance of it with the request data if the request method is POST. If the form is valid, the view function gets the username and the password. It then uses the authenticate() method to check if the user exists.

If so, it logs the user in using the login() method and redirects to the contact form. Otherwise, it displays an error message. However, if the request method is not POST, it simply displays an empty form.

For this project, we want the contact page to be assessed only by a registered user. That’s why we are redirecting to the contact page. More on this shortly.

The URL route will be like this:

    path('login', login, name='login'),

Be sure to import all the functions for the URLs. For the templates, it will be:

{% extends 'base.html' %}
{% load tailwind_filters %}

{% block content %}
  <h2>Login</h2>
  {% if messages %}
    <ul class="messages">
      {% for message in messages %}
        <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
      {% endfor %}
    </ul>
  {% endif %}
  <form method="post">
    {% csrf_token %}
    {{ form | crispy }}
    <button type="submit">Login</button>
  </form>
{% endblock %}

Logout

The view function for logging out is quite simple.

from django.contrib.auth import logout as auth_logout

def logout(request):
    if request.method == 'POST':
        auth_logout(request)
        messages.info(request, "You have successfully logged out.")
        return redirect("home")
    return render(request, 'logout.html')

The logout() method ensures the user is successfully logged out. A confirmation message is displayed and the user is redirected to the home page.

Here is the template:

{% extends 'base.html' %}

{% block content %}
  <h1 style="text-align:center">Logout</h1>
  <p>Are you sure you want to log out?</p>
  <form method="post">
    {% csrf_token %}
    <button type="submit">Yes</button>
  </form>
{% endblock %}

And the URL:

path('logout', logout, name='logout'),

We have authenticated the application. Users who want to benefit more from our services will be required to register on our platform. Notice how we renamed both our login view and logout() function to avoid name conflict in the application.

Conclusion

We are done with adding an authentication feature to the real estate application. I have demonstrated how I created a contact form that will only be displayed to users if they are authenticated. Check my GitHub page for the full code as well as some changes I made to the application.

Notice that there is no option to recover your password if lost. Well, that’s not within the scope of this project tutorial.

This simple demonstration will serve as a blueprint, so to speak, if you want to extend this application further with more advanced features.

🤠 Recommended: How I Built a Real Estate Application Using the Django Web Framework