• 12 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 3/2/22

Create a Login Page With Class-Based Views

Refactor the Login View to a Class-Based View

Next, you will recreate the functionality from the last chapter using class-based views.

What are class-based views?

As you may have guessed, class-based views represent views via classes rather than functions.  

In essence, class-based views split different functionalities of a view, for example, how it responds to  GET  vs.  POST  requests into different methods. 

Let's see how to convert the login_page  function-based view to a class-based view. 

Step 1: Refactor the Function-Based Login View to a Class-Based View

First, let's look at the current login view.

def login_page(request):
    form = forms.LoginForm()
    message = ''
    if request.method == 'POST':
        form = forms.LoginForm(request.POST)
        if form.is_valid():
            user = authenticate(
                username=form.cleaned_data['username'],
                password=form.cleaned_data['password'],
            )
            if user is not None:
                login(request, user)
                return redirect('home')
        message = 'Login failed!'
    return render(request, 'authentication/login.html', context={'form': form, 'message': message})

At the moment, this function executes regardless of the request method. Only the  if request.method ==  POST   statement is exclusive to  POST  requests. 

Now let's look at it as a class-based view:

from django.views.generic import View


class LoginPageView(View):
    template_name = 'authentication/login.html'
    form_class = forms.LoginForm
    
    def get(self, request):
        form = self.form_class()
        message = ''
        return render(request, self.template_name, context={'form': form, 'message': message})
        
    def post(self, request):
        form = self.form_class(request.POST)
        if form.is_valid():
            user = authenticate(
                username=form.cleaned_data['username'],
                password=form.cleaned_data['password'],
            )
            if user is not None:
                login(request, user)
                return redirect('home')
        message = 'Login failed!'
        return render(request, self.template_name, context={'form': form, 'message': message})

As you can see, the view logic is now nicely split into  get  and  post  methods. We have also taken some reused elements and set them as class attributes. The advantage of this is two-fold: it allows you to easily change these attributes from one place, making the code more maintainable, and it allows one to inherit from this class and change the template and form, making the class flexible and extensible

Step 2: Add the Class-Based View as a URL Pattern

To include a class-based view in a URL pattern, you need to use the method  as_view()  . This is one of the utility methods that you have access to due to inheriting from django.views.View.

# fotoblog/urls.py

urlpatterns = [
    ...
    path('', authentication.views.LoginPageView.as_view(), name='login'),
    ... 
]

You may find when making class-based views that you want to restrict the view to logged in users on your site. The easiest way to do this is to make your view inherit from the  django.contrib.auth.mixins.LoginRequiredMixin  , as well as the base  View  . 

And it's as simple as that! If you have decided to code along with this section, try out the login functionality now.

Implement Login With Generic Views

Class-based views came about as a way to create more easily extensible and flexible generic views. The point of generic views is to reduce the amount of code commonly repeated between different views. 

For example, if you wanted a view displaying information on a specific instance of a model, you might use the  DetailView  , or if you wanted to list all the instances of a model, the  ListView  .

As you may have guessed, you can also use these generic views to handle authentication. The previous chapters gave you insight into some of the low-level workings of authentication. In another project, you may find the ease and simplicity of the generic authentication views a better fit.

Let's rebuild our authentication system using generic views.

We'll use the  LoginView  generic view located in  django.auth.views  . When using the  LoginView,  all of the view logic is handled, so you just need to build a template and make sure some configuration is in place. 

 The key steps to recreate the login functionality with the  LoginView  are:

  • Implement a template that includes a form with the variable  form  .

  • Pass this template to the  LoginView.as_view()  method via the argument  template_name  .

  • Set the login page to automatically redirect logged in users by passing  LoginView.as_view()  the keyword argument  redirect_authenticated_user=True  . 

  • Set  LOGIN_REDIRECT_URL  in the settings to the success redirect page. 

It looks like the template you are already using for your login page will work nicely!

Let’s carry out these steps to implement  LoginView  now. 

Step 1: Configure the Settings

Add  LOGIN_REDIRECT_URL  to the settings. 

# fotoblog/settings.py

LOGIN_REDIRECT_URL = 'home'

Step 2: Add the URL Pattern and the View Configuration

As all of the view logic is already handled by the generic  LoginView  , you don't need to implement the view in  views.py  . Just add it straight into  urls.py  with some configuration.

# authentication/urls.py
from django.contrib.auth.views import LoginView

urlpatterns = [
   path('', LoginView.as_view(
           template_name='authentication/login.html',
           redirect_authenticated_user=True),
        name='login'),
   path('logout/', authentication.views.logout_user, name='logout'),
]

And that's it! You can even deleteforms.py  file as we are no longer using  LoginForm . 

Try testing out the login functionality now. You should find that it’s working as it was before.

Now that you've seen how to implement a generic view; it's time to implement some yourself.

Exercise: Implement the Generic Authentication Views

The  LoginView  is not the only authentication view that Django provides; there is a whole suite of generic views. 

For this exercise, you should implement the following views found in  django.contrib.auth.views  :

  • LogoutView  - you can delete the  logout_user  view we built once this is done.

  • PasswordChangeView

  • PasswordChangeDoneView 

Try it out!

Once you finish, or if you get stuck, you can see the correct implementation by checking out the the solution on GitHub.

Compare Class-Based Views and Function-Based Views

Wow, that was a lot to cover! Remember, there are three main approaches for implementing views:

  1. Function-based views - the request is handled by one function, with different request types such as  POST  and  GET  managed by conditional statements.

  2. Class-based views subclassing  django.views.generic.View  - the request is handled by a class, with different request types being managed by separate methods.

  3. Generic class-based views - the request is handled by a generic class-based view, which allows you to avoid writing commonly-used view logic.  

Which approach should I use, and when?

Here’s the bad news, there is no definitive answer! It all comes down to preference.

Daniel and Audrey Feldroys’ book Two Scoops of Django 3.x (an excellent resource for Django best practices) gives a good explanation of the schools of thought surrounding views. Here's a summary:

While the type of view to use often comes down to personal preference, there are generally three approaches that developers adhere to:

  1. Use generic views whenever and wherever you can, otherwise subclass  django.views.generic.View  . It reduces your workload at the cost of readability.

  2. Use class-based views but only subclassing  django.views.generic.View  . This approach is useful if you prefer the syntax of class-based views but want to avoid the readability problems of using somewhat opaque generic views.

  3. Only use function-based views as they are arguably easier to read and understand. Use class-based views if you share large chunks of code between views, in which case you should create a base class-based view and then subclass it. 

An exception to this is that the generic authentication views are often used in projects that primarily use a function-based view approach, as these provide a real time-saver at the start of a project.

For the rest of this course, I will use function-based views to build out the site, but feel free to try to implement these views as class-based views or even generic views if that is what you prefer.

Let's Recap!

  • Class-based views are an alternative approach to function-based views. Both are equally valid.

  • Generic views offer many shortcuts that help reduce the drudgery of repetitive tasks.

  • Django's authentication views are helpful generic views and can be used to avoid low-level authentication handling.

Now that we have implemented the authentication views, let's build the site's signup functionality.

Example of certificate of achievement
Example of certificate of achievement