Django and Ajax: Robust authentication and authorization system with real-time form validations for web applications
Tags: webdev, django, python, tutorial
Published: 2021-07-24 09:09:57

## Motivation

In contemporary times, real-time form validations and interactive application's user interfaces are prevalent and they are mostly incorporated in Software requirements. Being a framework for "perfectionists with deadlines", Django does help a bit by providing a classical server-side form validations for all projects that utilize its form module. However, these validations are not in real-time. You only get information about them after you submitted the form and the page is reloaded. Also, your application might require some other validation logic based on requirement which are not covered by Django out-of-box. Hence this article. We will be taken through a simple process of extending Django's default form validation logic and making sure users' inputs conform with them in real-time.

## Project Requirement Specification

Throughout this tutorial series, we'll be working towards implementing these requirements:

> Build a multi-user authentication system. A user can either be a **Student** or a **Lecturer**. For students, their usernames must start with **CPE** and must be **at least 9 characters long not counting any forward slash that it contains**. As for Lecturers, only their emails should be filled in and to be valid, all email addresses must end with **@futa.edu.ng**. Lecturers' usernames **should be autogenerated and must start with the words before `@` in the email address provided**. Email addresses must be **unique** and confirmed by sending confirmation mails upon registration and the **mails must support HTML**. **Until confirmation, no user is allowed to log in**. Time attacks must be addressed by **sending the mails asynchronously**. Users can either login with any of **Email/Password** or **Username/Password** combination. Password reset functionality should be incorporated as well. **At first login**, Lecturers should be prompted to complete their profiles.

😲 That was mouthful huh! It is from this end too 😫😩. Readers are enjoined to suggest other features and provide implementations in the comment section, I will be glad 🤗.

## Technology stack

We will not be using fancy JavaScript framework for this. Our stack comprises:

- Django (version 3.2.5)
- Pure JavaScript and jQuery(version 3.6.0, for AJAX)
- Pure CSS3 and Materialize CSS framework (version 1.0.0)
- HTML5

## Assumption

A simple prerequisite to follow along is basic familiarity with Django &mdash; like setting up virtual environment, bootstraping a project and application, a bit about its MVT architecture &mdash; JavaScript and CSS. You do not need to be an expert &mdash; I ain't one in any of the technologies.

> **NOTE**
> I have created a project named `authentication`, and an application, `accounts`. Both have been linked and `static` as well as `templates` configured. **You are enjoined to do these before proceeding**. Current folder structure is as follows:

```
└──  accounts/
 │  ├────   admin.py
 │  ├────   apps.py
 │  ├────   __init__.py
 │  └────   migrations/
 │  │  └────   __init__.py
 │  ├────  models.py
 │  ├────  tests.py
 │  └────  views.py
 └──  authentication/
 │  ├────  asgi.py
 │  ├────  __init__.py
 │  ├────  settings.py
 │  ├────  urls.py
 │  └────  wsgi.py
 ├──  manage.py
 ├──  Pipfile
 ├──  Pipfile.lock
 ├──  Procfile
 ├──  setup.cfg
 └──  static/
 │  └────  css/
 │  │  ├────  materialize.min.css
 │  │  └────  style.css
 │  └────  img/
 │  │  └────  sign-up-illustration.svg
 │  └────  js/
 │  │  ├────  init.js
 │  │  ├────  jquery.min.js
 │  │  └────  materialize.min.js
 └──  templates/
 │  └────  accounts/
 │  │  └────  index.html
 │  ├────  base.html
 │  └────  includes/
 │  │  ├────  _footer.html
 │  │  └────  _header.html

```

> You can get this full starter template structure from [github][1]. It should be noted that the structure also contains some configurations for sending emails with gmail. [Check this dev.to post for details about that.][2]

## Source code

The source code for this series is hosted on [github][3] or more expressed via:

{% github Sirneij/django_real_time_validation %}

The project is also live on heroku and can be accessed via this [django-authentication-app.herokuapp.com][6]

## Step 1: Build the `models.py` for the users.

To start with, let's handle this part of the specification:

> Build a multi-user authentication system. A user can either be a **Student** or a **Lecturer** ...

From that spec, we need a way to detect that someone is a student and another, a Lecturer. There are a variety of ways to accomplish this, however, we'll stick with the `AbstractUser` inheritance. This gives us the inherent robustness of the built-in django user model while allowing a nifty and clean extension. A good resource for working with this approach and others can be found in this [Vitor Freitas][4]'s article.

Now to the implemetation, open up your `accounts/models.py` file and transform it to something like this listing:

```python
# accounts > models.py

import uuid

from django.contrib.auth.models import AbstractUser
from django.db import models

LEVEL = (
    ("100L", "100L"),
    ("200L", "200L"),
    ("300L", "300L"),
    ("400L", "400L"),
)

ALIAS = (("Mr.", "Mr."), ("Mrs", "Mrs"), ("Dr.", "Dr."), ("Prof.", "Prof."))

GENDER = (
    ("Male", "Male"),
    ("Female", "Female"),
    ("Prefer not to mention", "Prefer not to mention"),
)


class User(AbstractUser):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    is_student = models.BooleanField(default=False)
    is_lecturer = models.BooleanField(default=False)
    alias = models.CharField(choices=ALIAS, max_length=5, null=True, blank=True)
    level = models.CharField(choices=LEVEL, max_length=11, null=True, blank=True)
    gender = models.CharField(choices=GENDER, max_length=22)
    has_logged_in = models.BooleanField(default=False)

    def __str__(self):
        return str(self.id)
```

Do not run migrations yet 🛑. If you do, you are likely to be greeted with something like:

```bash
┌──(sirneij@sirneij)-[~/Documents/Projects/Django/django_real_time_validation]
└─$[sirneij@sirneij django_real_time_validation]$ python manage.py makemigrations
SystemCheckError: System check identified some issues:

ERRORS:
accounts.User.groups: (fields.E304) Reverse accessor for 'accounts.User.groups' clashes with reverse accessor for 'auth.User.groups'.
	HINT: Add or change a related_name argument to the definition for 'accounts.User.groups' or 'auth.User.groups'.
accounts.User.user_permissions: (fields.E304) Reverse accessor for 'accounts.User.user_permissions' clashes with reverse accessor for 'auth.User.user_permissions'.
	HINT: Add or change a related_name argument to the definition for 'accounts.User.user_permissions' or 'auth.User.user_permissions'.
auth.User.groups: (fields.E304) Reverse accessor for 'auth.User.groups' clashes with reverse accessor for 'accounts.User.groups'.
	HINT: Add or change a related_name argument to the definition for 'auth.User.groups' or 'accounts.User.groups'.
auth.User.user_permissions: (fields.E304) Reverse accessor for 'auth.User.user_permissions' clashes with reverse accessor for 'accounts.User.user_permissions'.
	HINT: Add or change a related_name argument to the definition for 'auth.User.user_permissions' or 'accounts.User.user_permissions'.
```

To fix this, we need to append this line to our `settings.py` file:

```python
# authentication > settings.py
...
AUTH_USER_MODEL = "accounts.User"
...
```

Now, run migrations.

```bash
┌──(sirneij@sirneij)-[~/Documents/Projects/Django/django_real_time_validation]
└─$[sirneij@sirneij django_real_time_validation]$ python manage.py makemigrations

Migrations for 'accounts':
  accounts/migrations/0001_initial.py
    - Create model User
```

And then `migrate`.

Test your application to ensure everything works 👏.

You should see something like this in your browser:

![Home page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y72wd43qt3wm5505nwjy.png)

You can grab the full code for this part on [github][5].

Let's draw the curtain here for now. See you in the next part 😎.

## Outro

Enjoyed this article, consider [contacting me for a job, something worthwhile or buying a coffee ☕](mailto:sirneij@gmail.com).

[1]: https://github.com/Sirneij/django_real_time_validation/tree/a1ae83e085ded3dac43a75d2ac6a4f354ed99aea "Starter folder structure"
[2]: https://dev.to/abderrahmanemustapha/how-to-send-email-with-django-and-gmail-in-production-the-right-way-24ab "Send emails with Django and Gmail , a better way"
[3]: https://github.com/Sirneij/django_real_time_validation "Full project source code"
[4]: https://simpleisbetterthancomplex.com/tutorial/2018/01/18/how-to-implement-multiple-user-types-with-django.html "How to Implement Multiple User Types with Django"
[5]: https://github.com/Sirneij/django_real_time_validation/tree/9798a33b7ffcd46851318d7764e3f5417c4de89f "Full code for the first part"
[6]: https://django-authentication-app.herokuapp.com/ "Live version of the application"