Django Class-Based Views, Mixins, LoginRequiredMixin and Custom Mixins
In this post, we’ll be learning about Django Class-Based Views, Mixins, LoginRequiredMixin and Custom Mixins.
Table of Contents
- Introduction
- Built-in Mixins
- AccessMixin Mixins
- LoginRequiredMixin Mixins
- PermissionRequiredMixin Mixins
- Creating Custom Mixin
- Conclusion
Introduction
Django Class-based views make it easy to handle business logic inside the class and to take it further and expanding its functionality by inheriting other classes which provide a solution to specify purpose mixins were introduced.
These mixins can also be tailored with specified requirement or developers can create custom mixins.
Built-in Mixins
Some of the built-in mixins provided are :
- Simple Mixins
- Single object Mixins
- Multiple object Mixins
- Editing Mixins
- Date-based Mixins
- Authentication Mixins :
AccessMixin
,LoginRequiredMixin
,PermissionRequiredMixin
.
Click here for Django official for docs on mixins.
AccessMixin Mixins
The AccessMixin
is an abstract class for mixins and is inherited by LoginRequiredMixin
and PermissionRequiredMixin
.
Attributes
- login_url: This is a redirect URL to the login page.
- permission_denied_message: Outputs message if permission is denied.
- raise_exception: Raises an exception if a user has no permission.
- redirect_field_name: This gets the URL where the user must be redirected.
Methods
- get_login_url(): Returns login_url if not specified than returns
settings.LOGIN_URL
. - get_permission_denied_message: Returns given
permission_denied_message
attribute. - get_redirect_field_name: Returns given
redirect_field_name
attribute. - handle_no_permission: This method is called when the user has no access permission and must be redirected to login URL.
Snippet from Django Documentation
Below is a snippet of AccessMixin
taken from Django docs. This is to show you what logic is performed inside the AccessMixin
class.
class AccessMixin: """ Abstract CBV mixin that gives access mixins the same customizable functionality. """ login_url = None permission_denied_message = '' raise_exception = False redirect_field_name = REDIRECT_FIELD_NAME def get_login_url(self): """ Override this method to override the login_url attribute. """ login_url = self.login_url or settings.LOGIN_URL if not login_url: raise ImproperlyConfigured( '{0} is missing the login_url attribute. Define {0}.login_url, settings.LOGIN_URL, or override ' '{0}.get_login_url().'.format(self.__class__.__name__) ) return str(login_url) def get_permission_denied_message(self): """ Override this method to override the permission_denied_message attribute. """ return self.permission_denied_message def get_redirect_field_name(self): """ Override this method to override the redirect_field_name attribute. """ return self.redirect_field_name def handle_no_permission(self): if self.raise_exception or self.request.user.is_authenticated: raise PermissionDenied(self.get_permission_denied_message()) return redirect_to_login(self.request.get_full_path(), self.get_login_url(), self.get_redirect_field_name())
This snippet is taken from Django documentation click here to view its details in Django docs.
LoginRequiredMixin Mixins
This mixin checks if the requested user is logged-in. It inherits AccessMixin
and over-writes dispatch
with request.user.is_authenticated
condition.
To use this mixin import LoginRequiredMixin
from from django.contrib.auth.mixins import LoginRequiredMixin
.
Example
In views.py.
from django.contrib.auth import authenticate, login, logout class LoginView(View): def get(self, request, *args, **kwargs): user = authenticate(request, username='root', password='123456') if user is not None: login(request, user) return HttpResponse("Login View") class LoginOutView(View): def get(self, request, *args, **kwargs): logout(request) return HttpResponse("Log-out View") from django.contrib.auth.mixins import LoginRequiredMixin class ProfileView(LoginRequiredMixin, View): login_url = '/cbs/login' def get(self, request, *args, **kwargs): return HttpResponse("User Profile View")
In urls.py.
from django.urls import path, include from class_based_views import views app_name = 'class_based_views' urlpatterns = [ path('login', views.LoginView.as_view(), name='login'), path('logout', views.LoginOutView.as_view(), name='logout'), path('user-profile', views.ProfileView.as_view(), name='user-profile'), ]
The below code is a simple snippet for how we log in a user. In order to understand we have made it simple and clean as this is for demonstration for LoginRequiredMixin
.
You have ProfileView
which inherits LoginRequiredMixin
and View
now to see profile one must be logged in and to check whether the user is logged-in is determined by LoginRequiredMixin
.
The LoginRequiredMixin
just overrides the dispatch()
method of View
class.
PermissionRequiredMixin Mixins
This mixin check permission for every user with permission_required
attribute. The attribute permission_required
contains the permissions users must have in order to access this view.
Example
permission_required = 'blogmodel.view_blogmodel'
The above permission blogmodel.view_blogmodel
requires the user to have view_blogmodel
permission and blogmodel
is the model name.
Attributes
- permission_required: This attribute takes a string as well as a tuple for specifying multiple permissions.
Methods
- get_permission_required(): Returns the attribute
permission_required
. - has_permission(): Checks if users have all the permissions specified in
permission_required
and internally callsself.request.user.has_perms()
methods. - dispatch(): Returns parent
dispatch()
method is user has permission.
Example: PermissionRequiredMixin Mixins
In views.py.
from django.contrib.auth.mixins import PermissionRequiredMixin class EditBlogView(PermissionRequiredMixin, View): permission_required = 'blogmodel.view_blogmodel' def get_permission_required(self): perms = super(EditBlogView, self).get_permission_required() return perms def has_permission(self): perms = super(EditBlogView, self).has_permission() return perms def get(self, request, *args, **kwargs): return HttpResponse("Edit Blog View")
In urls.py.
from django.urls import path from class_based_views import views app_name = 'class_based_views' urlpatterns = [ path('edit-blog', views.EditBlogView.as_view(), name='edit-blog'), ]
We have specified our permission_required
and defined blogmodel.view_blogmodel
which means that the user can view the blog.
Methods such as get_permission_required
and has_permission
can be overridden and we can specify our conditions in those extending its functionality.
If a user does not have permissions than he will be redirected to login_url
or settings.LOGIN_URL
if login_url
is None.
Creating Custom Mixin
In our post, on Django Class-based views | Decorators, Methods, Template and Redirect view you have submitted form using class-based view and you have created a function that would handle file upload.
Here we’ll consider the same snippet as an example and refactor it by creating our own custom mixin for file upload.
In mixins.py.
from django.conf import settings import os from django.core.files.storage import FileSystemStorage class FileUploadMixin(object): upload_path = "/static/uploads" def upload(self, file_list, allowed_mime_types=[]): if not os.path.exists("{}/{}".format(settings.BASE_DIR,dir_path)): os.makedirs("{}/{}".format(settings.BASE_DIR,self.upload_path),exist_ok = True) if not isinstance(file_list, list): file_list=list(file_list) directory = os.path.join(settings.BASE_DIR, dir_path) info={ "files" : [] } for file in file_list: file_name= file._name file_mime = file.content_type.split('/')[1] path= "{}{}".format(directory, file_name) is_allowed_to_upload=False #check allowed mime types if that file does not belong to mime type remove it if len(allowed_mime_types) > 0: if file_mime in allowed_mime_types: is_allowed_to_upload=True else: is_allowed_to_upload=True if is_allowed_to_upload is True: with open(path, 'wb+') as destination: for chunk in file.chunks(): destination.write(chunk) info["files"].append(file_name) return info
The FileUploadMixin
is a mixin which takes python object as a parameter. It has an attribute upload_path
which is used to upload file to that path user can also specify their custom path.
The Method upload
takes a few arguments they are:
- file_list: which has list files to be uploaded.
- allowed_mime_types: This is a validation check and only match extensions will be uploaded.
The other miscellaneous logic performs operations such as checking directory exists, mime type validation checks and returning uploaded files are done by upload
method.
This helps to reduce code redundancy.
In views.py.
from class_based_views.mixins import FileUploadMixin class NewBlogView(FileUploadMixin, View): template_path = "cbs/new_blog_view.html" upload_path = "/static/uploads" def get(self, request): ctx={ "users" : User.objects.values(), "status" : BlogModel.BLOG_STATUS } return render(request, self.template_path,ctx); def post(self, request): result={} if request.FILES.get("path") is not None: file_list = request.FILES.getlist("path") result = self.upload(file_list, allowed_mime_types=[]) id = request.POST.get('blog_id') or None blog, created = BlogModel.objects.update_or_create( pk=id, defaults={ 'user' : User.objects.filter(pk=request.POST["user"]).get(), 'title' : request.POST["title"], 'content' : request.POST["content"], 'status' : "PUBLISH", }) if result.get('files'): for file in result.get('files'): blog.blogs.create(**{ "blog" : blog, "path" : file, }) return redirect(reverse("class_based_views:new-blog-view"))
In view NewBlogView
we have first added FileUploadMixin
and after that View
. This is an important use-case and the View classes must be always in MRO
order.
First, there must be mixins and View
last.
In urls.py.
from django.urls import path from class_based_views import views app_name = 'class_based_views' urlpatterns = [ # custom mixins path('new-blog-view', views.NewBlogView.as_view(), name='new-blog-view'), ]
Conclusion
So you have come to the conclusion part of our Django Class-Based Views, Mixins, LoginRequiredMixin and Custom Mixins post.
If you like this then please share and for queries comment below. We’ll reach to you soon.
Related Posts
- Django Querysets | One To One Foreign Key, Inner Joins, Query Filtering
- Django Querysets | Backward, Reverse and ManyToMany Relationships




