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)
- Use the following command to clone the directory.
$ git clone https://github.com/ArsalanShahid116/Django-MovieStore.git
- View all the tutorials in this series at DirectMe
- 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.