Securing the Application

Now that we can view our notes on the website, we are going to want a login system. Currently anyone can navigate to our website and see all of our notes. Not ideal. To secure our site we will be using a combination of Djangos built-in auth module and crispy forms. First off we will add the auth modules URLs to the project so open up the URLs file in the project directory and add the following. This will mean that all the account functions, such as logging in/out, password changes etc. are located underneath “/account/” of our website.

from .views import LoginView

path('account/', include('django.contrib.auth.urls')),
path('account/login', LoginView.as_view(), name='login')

We are overriding the default login page with our own one as we are going to be using a crispy form to make it look far nicer. We will do this using a view so we need to create a views.py file inside the project directory DjangoNotes/. Once you’ve opened the file, copy the following into it:

from django.contrib.auth import authenticate, login
from django.urls import reverse_lazy
from django.views import generic

from .forms import LoginForm


class LoginView(generic.FormView):
    form_class = LoginForm
    success_url = reverse_lazy('home')
    template_name = 'accounts/login.html'

    def form_valid(self, form):
        username = form.cleaned_data['username']
        password = form.cleaned_data['password']
        user = authenticate(username=username, password=password)

        if user is not None and user.is_active:
            login(self.request, user)
            return super(LoginView, self).form_valid(form)
        else:
            return self.form_invalid(form)

The class tells Django what to do and how to do when someone navigates to the login page. The form class used is one we will create shortly. If the user successfully logs in then they will be redirected to ‘home’ (root of our website). It also specifies which template to load. Once the form is submitted is does some basic data cleaning and attempts to authenticate the user against the database of users.

We now need to create our Form object. This defines what fields the form will have and the attributes it will contain. Create a new file in the same place as the views file and copy the following code to it. All we are doing here is defining 2 fields, username and password, and the submit button. The submit button has some extra code defining how it will look. In this case we want it to be a primary button.

from django.contrib.auth.forms import AuthenticationForm

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, ButtonHolder, Submit


class LoginForm(AuthenticationForm):
    def __init__(self, *args, **kwargs):
        super(LoginForm, self).__init__(*args, **kwargs)

        self.helper = FormHelper()
        self.helper.layout = Layout(
            'username',
            'password',
            ButtonHolder(
                Submit('login', 'Login', css_class='btn-primary')
            )
        )

Now we have defined the login system we can force the user to login when attempting to view either of our 2 pages. This can be done incredibly simple with the use of decorators. Edit your views file so it looks like the following. This tells the login system to direct the user to the login_url if they access the page with authorization.

[ ... ]
from django.contrib.auth.decorators import login_required

@login_required(login_url="/account/login")
def note_list(request, tag_slug=None):
    [ ... ]

@login_required(login_url="/account/login")
def note_detail(request, note):
    [ ... ]