Symons
Passionate developer with 3 years of building web apps with python Django. Symons is a graduate from Maseno University with Bsc Computer Science degree. He is mostly happy when his students make strides

Django Authentication(login/signup): chapter 6

 

Hi, in the last chapter we created models and learnt about the admin site. . lastly, we loaded data dynamically in templates, check it out here if you haven’t gone through it. Here we will learn about django authentication.

Before we begin I hope you are safe wherever you are. Just a bit of advice: to overcome Covid 19 Pandemic, we need to have self-discipline and do what our government has asked us to do as well as world health organization’s guidelines.  Personally I’m limiting unnecessary travel and working from home. Keep safe!

Fine, in this post, we want to build on an authentication system that will allow us to have three users to signup differently and have restricted access meaning support team can’t have the same rights as director. Well, there are many ways of implementing this, for this tutorial I referred to a post by Vitor Freitas you can read it here  I choose Option 4: Creating a Custom User Model Extending AbstractUser. Let us start working.

Add the following to models.py

from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    is_client = models.BooleanField(default=False)
    is_support = models.BooleanField(default=False)
    is_director = models.BooleanField(default=False)
    is_approved = models.BooleanField(default=False)

This is what will happen when a user is registering we will turn either is_client, is_support or is_director  to be true. These fields will be added to the default user model and we are not creating a new model.

And base urls.py (project level): Sticketing/urls.py 

from django.contrib import admin
from django.urls import path, include
from ticketing.views import views #new

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('ticketing.urls')),
	# new urls 
    path('accounts/', include('django.contrib.auth.urls')),
    path('accounts/signup/clients/', views.clientsSignUp.as_view(),name='client_signup'),
    path('accounts/signup/support/',views.supportSignUp.as_view(),name='support_signup'),
    path('accounts/signup/director/',views.directorSignUp.as_view(),name='director_signup'),
]

As you must have guessed, our next stop is views but before that let us try and understand what’s happening here.

path('accounts/', include('django.contrib.auth.urls')),  this line includes URLs from default Django auth app just like we were doing with ticketing URLs. It has the following URLs by default:

We are again adding normal URLs but this time they are referring to class-based views you’ll see how they are made later on

accounts/login/ [name='login']
accounts/logout/ [name='logout']
accounts/password_change/ [name='password_change']
accounts/password_change/done/ [name='password_change_done']
accounts/password_reset/ [name='password_reset']
accounts/password_reset/done/ [name='password_reset_done']
accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/reset/done/ [name='password_reset_complete']

Lets now create the views. ticketing/views.py 

You can already see the beauty of django authentication.

from django.shortcuts import render, redirect
from .models import Ticket, User
from ticketing.forms import ClientSignUpForm, SupportSignUpForm, DirectorSignUpForm
from django.views.generic import (CreateView)


class clientsSignUp(CreateView):
    model = User
    form_class = ClientSignUpForm
    template_name = 'registration/signup_form.html'

    def get_context_data(self, **kwargs):
        kwargs['user_type'] = 'client'
        return super().get_context_data(**kwargs)

    def form_valid(self, form):
        user = form.save()
        return redirect('client_home')


class supportSignUp(CreateView):
    model = User
    form_class = SupportSignUpForm
    template_name = 'registration/signup_form.html'

    def get_context_data(self, **kwargs):
        kwargs['user_type'] = 'support'
        return super().get_context_data(**kwargs)

    def form_valid(self, form):
        user = form.save()
        return redirect('support_home')


class directorSignUp(CreateView):
    model = User
    form_class = DirectorSignUpForm
    template_name = 'registration/signup_form.html'

    def get_context_data(self, **kwargs):
        kwargs['user_type'] = 'director'
        return super().get_context_data(**kwargs)

    def form_valid(self, form):
        user = form.save()
        return redirect('director_home')


def home(request):
    return render(request, 'home.html', {})


def clients(request):
    return render(request, 'clients/home.html', {})


def support(request):
    tickets = Ticket.objects.all()  #storying query results on tickets variable
    return render(request, 'support/home.html', {'tickets': tickets})


def director(request):
    return render(request, 'director/home.html', {})

What we are doing here creating a class corresponding to the ones in URLs and telling it which model we want to create, which form to use and which template to render.

The first function here get_context_data() is passing the type of person registering to the templates.

The second method form_valid() is simply saving the form and redirecting to the right page. I’m actually using it for redirecting now but we will make good use of it soon.

Next, we go to forms. on the same directory as views.py create a file called forms.py and have the following in it:

ticketing/forms.py 

from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.db import transaction

from ticketing.models import User, Ticket


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

    @transaction.atomic
    def save(self, commit=True):
        user = super().save(commit=False)
        user.is_client = True
        user.is_approved = False
        if commit:
            user.save()
        return user


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

    @transaction.atomic
    def save(self, commit=True):
        user = super().save(commit=False)
        user.is_support = True
        user.is_approved = False
        if commit:
            user.save()
        return user


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

    @transaction.atomic
    def save(self, commit=True):
        user = super().save(commit=False)
        user.is_director = True
        user.is_approved = False
        if commit:
            user.save()
        return user

As you can see with usercreationform you just pass model you want to create form for, and fields. Then before we override save method and not saving immediately until after saving the user type.

Lastly, let us no go to templates. We will just create a folder called registration inside the templates folder then inside the registration create an html file called signup_form.html

templaes/registration/signup_form.html

{% extends 'base.html' %}

{% block content %}
<div class="row">
    <div class="col-md-8 col-sm-10 col-12">
        <h2>Add a {{ user_type }}</h2>
        <form method="post" novalidate>
            {% csrf_token %}
            <input type="hidden" name="next" value="{{ next }}">
            {{ form }}
            <button type="submit" class="btn btn-success">Submit</button>
        </form>
    </div>
</div>
{% endblock %}

We should be good!

Fire up your server and lets test.

(venv) symons@symons-macbookair:~/STICKETING$ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
March 20, 2020 - 19:11:15
Django version 2.2.10, using settings 'Sticketing.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Go to

127.0.0.1:8000/accounts/signup/clients/ 

You should be able to register client

 

djjango authentication

Messy but works, next we’ll style the forms later in the coming chapters

Also, try the following URLs

127.0.0.1:8000/accounts/signup/support/

127.0.0.1:8000/accounts/signup/director/

In the next chapter we will be learning how to better use signup, and login.

And styling the forms up with crispy-forms and more.

That was all about django authentication.

Let us meet in the next chapter, give me your feedback. Also, comment below and help others if they are stack

Get the code from github

You may join my whatsapp group

or Contact me personally info@omborisymons.com

close

You may also like...

2 Responses

Leave a Reply

Your email address will not be published. Required fields are marked *