Arsalan Shahid

Django Custom Managers and User Login/Registration portal – MovieStore Part 4

My dear reader, how are you? السلام عليكم

Be not afraid of growing slowly, be afraid of standing still – Chinese Proverb

This post is a continuation of Django tutorial series using an example MovieStore. We will continue to extend our MovieStore application tutorial by adding custom managers for myMovie and Person and creating a user login and registration portal.


In order to follow this tutorial, I suggest the following:

MovieStore GitHub Repository – (DirectMe)

  1. Use the following command to clone the directory.
    $ git clone https://github.com/ArsalanShahid116/Django-MovieStore.git
  2. View all the tutorials in this series at DirectMe
  3. Set your GitHub repo to MovieStore Part 3 using the following command:
Django-MovieStore$ git fetch origin 0b106a4eada843535b4fe5ae763464c0da39a4c2

Adding Custom Managers for Model Classes

Django allows you to create custom managers for a model class which helps to limit the database queries and make the overall execution fast with less memory occupancy. If we do not create custom managers for classes then the database will be flooded with a lot of queries and custom managers allow us to create smaller QuerySets. Let us now create a custom manager for Person class and call it PersonManager as shown below: 

# goto MoviesApp/models.py and add the following 

class PersonManager(models.Manager):
    def all_with_prefetch_movies(self):
        qs = self.get_queryset()
        return qs.prefetch_related(
            'directed',
            'writing_credits',
             )

We use a function prefetch_related() to just use one query for accessing all the movies for related to a person in a database. Otherwise, it would be N queries to the database in case of a person with N movies. We define a function with all_with_prefetch_movies() and make it a default person manager for our class. We also added it in Person class as shown below.

class Person(models.Model):
    ...
    ...

    objects = PersonManager() # add this
    
    class Meta:
        ordering = ('last_name', 'first_name')
    ...
    ...

Similarly, we now create a manager for myMovie class as well and call it as MovieManager() as follows:

class MovieManager(models.Manager):
    def all_with_related_persons(self):
        qs = self.get_queryset()
        qs = qs.select_related(
            'director')
        qs = qs.prefetch_related(
            'writers')
        return qs

class myMovie(models.Model):
    ...
    ...
    objects = MovieManager() # add this
    ...
    ...

In the MovieManager we used select_related() which is pretty much related to prefetch_related() but it is used when the relationship leads to one model (say whenever you use ForeignKey field). On the other hand, use the prefetch_related(), function when the relationship may occur between more than one model (say whenever you use ManytoMany field).

To make use of MovieManager(), we just need to make a minor change in MovieDetail view as follows.

# goto moviesApp/views.py and add
class MovieDetail(DetailView):
    queryset = (
            myMovie.objects.all_with_related_persons()
            )

This will change nothing but limit the number of queries and won’t have to query the database each time the related Person model instance is required.


Creating a User Application and Adding a Registration View

We will now create a user login and registration portal using Django built-in functionalities. We will create a new user app first using the commands below:

(myenv)Django-MovieStore/MyMovies$ python3 manage.py startapp user 

# open movies/settings.py and add the following

INSTALLED_APPS = [
    'user' # make sure you add 'user' before admin app
    'moviesApp',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

For a typical app, we would need to define models for the newly created app. But, Django has its built-in auth app which already has the user model defined for us to use.

We will not create a user registration view as follows:

# open user/views.py and add the following

from django.contrib.auth.forms import (
    UserCreationForm,
)
from django.urls import (
    reverse_lazy,
)
from django.views.generic import (
    CreateView,
)

class RegisterView(CreateView):
    template_name = 'user/register.html'
    form_class = UserCreationForm
    success_url = reverse_lazy(
        'moviesApp:MovieList')

RegisterView uses and extends CreateView with which defining GET and POST functions become unnecessary. UserCreationForm is the model that form_class should use. register.html is the template that should be used. Finally, with successful registration, the user should be directed to URL followed by success_url.

We will now add a template as MyMovies/user/templates/user/register.html and add the following program into it.

{% extends "base.html" %}

{% block main %}
<h1> Register for MovieStore </h1>

<form method="post">
        {{ form.as_p }}
        {% csrf_token %}
        <button type="submit" class="btn btn-primary">
                Register
        </button>
</form>
{% endblock %}

We now add a URL to our RegisterView as follows:

# create a urls.py file in user app and add the following

from django.urls import path
from django.contrib.auth import views as auth_views
from django.contrib.auth import urls

from user import views

app_name = 'user'
urlpatterns = [
    path('register',
        views.RegisterView.as_view(),
        name='register'),
]

We now have to also link the urls.py for user app to project configuration urls.py by adding the following:

# goto movies/urls.py and extend the following 

from django.contrib import admin
from django.urls import path, include

import moviesApp.urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('user/', include(user.urls, namespace='user')), # add this
    path('', include(moviesApp.urls, namespace='moviesApp')),
]

Extending user app with Login and Logout Features

Django auth app provides a lot of views to make user authentication and management easier. We will use the built-in views and add update the URL configurations to direct to log-in and log-out views as shown below:

# open user/urls.py and extend it with following views

urlpatterns = [
    path('register',
         views.RegisterView.as_view(),
         name='register'),
    path('login/', # add this
         auth_views.LoginView.as_view(),
         name='login'),
    path('logout/', # add this 
         auth_views.LogoutView.as_view(),
         name='logout'),
]

We will now create a log-in template as shown below:

# add a new template in user/templates/registration/login.html

{% extends "base.html" %}

{% block title %}
Login - {{ block.super }}
{% endblock %}

{% block main %}
<form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button  class="btn btn-primary">
                Log In
        </button>
</form>
{% endblock %}

On successful login, a user should be directed to moviesApp:MovieList view. We need to configure it in settings.py of project configuration by adding the following line

# open movies/settings.py and add the following

LOGIN_REDIRECT_URL = 'moviesApp:MovieList' 
LOGIN_URL = 'user:login'

We now add a logout view. We can either create a logged-out.html template or if we do not create it and we have out admin app installed in Django project then on a user logout we would be directed to the default logout view.

However, in this tutorial, we will create a logged-out template as follows

# create a user/templates/registration/logged-out.html and add the following program

{% extends "base.html" %}

{% block title %}
Logged out
{% endblock %}

{% block main %}
<h1>Logged out</h1>
<p>Visit Again Soon</p>
{% endblock %}

In the next post, we will continue to extend our MovieStore tutorial by adding some functionality by using user and moviesApp together by creating a voting system and allowing users to vote for a particular movie.

I hope you find this tutorial useful. If you find any errors or feel any need for improvement, let me know in your comments below.

Signing off for today. Stay tuned and I will see you next week! Happy learning.

Exit mobile version