This article is the 7th in the series exploring Django Class-Based Views (CBV), covering how to utilize Mixins to reuse common functionalities and efficiently implement permission management and login checks. In the previous article (6th), we automated simple page rendering and redirection using TemplateView and RedirectView. Now, let's learn how to maximize the reusability and extensibility of CBVs.

Click the link below for the previous article!

How to Use TemplateView & RedirectView


“Increase code reusability with Django Mixins and implement simple permission and login checks!”


1. Why Do We Need Mixins?

Mixin Concept Diagram - Class Structure with Functional Blocks

Django CBV offers robust reusability on its own, but what if there are common functionalities needed across multiple views? For instance, it's often necessary to require login to access certain pages or to restrict access to users with specific permissions. This is where the Mixin concept comes in.

A Mixin uses the concept of multiple inheritance to inject the same methods or attributes into several classes. In Django CBV, by inheriting a Mixin class that implements specific functionalities, we can reduce duplicate code and enhance maintainability.

Examples of common Mixin usage:

  1. Permission Management: Check if a user accessing a specific view is logged in or possesses certain permissions.
  2. Form Processing Logic: Data preprocessing or postprocessing logic required by multiple forms.
  3. Template Context: Additional context data that needs to be shared across all views.
  4. Logging: Add logging functionality before and after executing a specific view.

2. A Representative Mixin in Django: LoginRequiredMixin

One of the most commonly used Mixins is LoginRequiredMixin. As the name suggests, it enforces that users must be logged in to access the view.

2.1 How to Use LoginRequiredMixin

The LoginRequiredMixin is included in the django.contrib.auth.mixins module.

# views.py
from django.views.generic import ListView
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import Post # Example Model

class MyPostListView(LoginRequiredMixin, ListView):
    model = Post
    template_name = 'posts/my_posts.html'
    context_object_name = 'posts'

    # Optional: Set the URL to redirect to if the user is not logged in
    # login_url = '/accounts/login/' # Default is settings.LOGIN_URL
    # redirect_field_name = 'next'  # Name of the query parameter used to return to the original page after login (default)
  • Inheritance Order: Inherit LoginRequiredMixin first, followed by Django’s Generic View (in this case, ListView). Mixins are primarily used for adding functionalities, so it is customary to place them before the core view class.
  • LoginRequiredMixin automatically redirects users who are not logged in to the login page specified in settings.LOGIN_URL. Upon successful login, it returns to the originally requested page through the next query parameter.

2.2 Connecting in urls.py

Views using LoginRequiredMixin are connected in urls.py in the same manner as regular CBVs.

# urls.py
from django.urls import path
from .views import MyPostListView

urlpatterns = [
    path('my-posts/', MyPostListView.as_view(), name='my_posts'),
]

Now, when accessing the /my-posts/ path, users who are not logged in will be redirected to the login page.


3. Mixins for Permission Management: PermissionRequiredMixin & UserPassesTestMixin

If LoginRequiredMixin enforces login, then PermissionRequiredMixin and UserPassesTestMixin require users to have specific permissions or meet certain conditions to access the view.

Login & PermissionMixin Concept Diagram - Access Permissions Check

3.1 How to Use PermissionRequiredMixin

This Mixin integrates with Django’s permission system, allowing access only for users with specific permission.

# views.py
from django.views.generic import CreateView
from django.contrib.auth.mixins import PermissionRequiredMixin
from .models import Article
from .forms import ArticleForm

class ArticleCreateView(PermissionRequiredMixin, CreateView):
    model = Article
    form_class = ArticleForm
    template_name = 'articles/article_form.html'
    success_url = '/articles/'

    # Specify the required permission in 'app_label.permission_codename' format
    permission_required = 'app_label.add_article'
    # To allow if the user has any of the multiple permissions:
    # permission_required = ('app_label.add_article', 'app_label.change_article')
    # raise_exception = True # Raise a 403 Forbidden error if permission is denied (default is redirect to login page)
  • permission_required: Specify the required permissions as a string (or tuple). Typically, use the permissions automatically generated per model by Django’s auth app (add_model, change_model, delete_model, view_model).

3.2 How to Use UserPassesTestMixin

Use this Mixin when you want to check more complex or custom conditions. For example, it can be useful for allowing users to modify only the comments they've authored or restricting access to certain groups of users.

# views.py
from django.views.generic import UpdateView
from django.contrib.auth.mixins import UserPassesTestMixin
from .models import Comment
from .forms import CommentForm

class CommentUpdateView(UserPassesTestMixin, UpdateView):
    model = Comment
    form_class = CommentForm
    template_name = 'comments/comment_form.html'
    success_url = '/comments/'

    # Override test_func method to check conditions
    def test_func(self):
        comment = self.get_object()
        # Check if the currently logged-in user is the author of the comment
        return self.request.user == comment.author or self.request.user.is_superuser

    # raise_exception = True # Raise a 403 Forbidden error if the test fails
  • Override the test_func() method to allow access if it returns True and deny access if it returns False.
  • You can access the currently logged-in user object through self.request.user.

4. Creating Custom Mixins

In addition to the Mixins provided by Django, you can create your own custom Mixins for reuse across multiple views. For example, let's create a Mixin that adds the current_year context needed on all pages.

# common/mixins.py
from datetime import datetime

class CurrentYearMixin:
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['current_year'] = datetime.now().year
        return context

# views.py (Example of application)
from django.views.generic import TemplateView
from .common.mixins import CurrentYearMixin

class MyCustomPageView(CurrentYearMixin, TemplateView):
    template_name = 'my_custom_page.html'

# my_custom_page.html (Using in the template)
<footer>&copy; {{ current_year }} My Website</footer>
  • CurrentYearMixin overrides the get_context_data method to add current_year to the context. It is essential to call super().get_context_data(**kwargs) to maintain the context data from the parent class (or other Mixins).

5. Comparison with FBVs

Functionality FBV (Function-Based View) CBV + Mixin
Login/Permission Check Use @login_required, @permission_required decorators. <br> Custom conditions must be written directly in the function, e.g., if not request.user.is_authenticated: return redirect(...). Simple application through inheritance of LoginRequiredMixin, PermissionRequiredMixin, UserPassesTestMixin. <br> Similar to decorators but more integrated into the CBV structure.
Code Reusability Separate common logic into a separate function or directly write function decorators. <br> Susceptible to duplicated code. Module functionality through Mixin, injecting with multiple inheritance. <br> Much more structured and reusable.
Code Readability Condition checking logic may repeat in each function. Easily understand what functionalities (permissions, login, etc.) are applied just by looking at the inheritance list of the view class.
Maintainability May need to modify multiple functions when adding new features. Changes to the Mixin class automatically apply to all views that use that Mixin.
Development Productivity (Core Keyword) “Individual processing for each function, potential for repetitive work.” “Modularization, easy extension of features, adherence to the DRY (Don't Repeat Yourself) principle, increased productivity.”

6. Conclusion and Next Article Preview

Mixins are a key factor that unleashes the true power of Django class-based views. Through them, we can maximize code reusability, modularize common functionalities to reduce the complexity of view classes, and implement essential elements of web applications, such as authentication and permission management, very efficiently.

Now we have explored almost all core aspects of Django CBV, from basic usage to complex CRUD, and feature extension through Mixins.

In the next article (the 8th in the series), we will summarize how to apply the CBVs we've discussed to real projects, how to combine them in complex scenarios, and comprehensively review the advantages and limitations of CBVs as we conclude this series.


Revisit Previous Articles

  1. Exploring Class-Based Views (CBV) Series #1 – The Reason for Moving from FBV to CBV and Developer Mindset
  2. Exploring Class-Based Views (CBV) Series #2 – Understanding Django’s Basic View Class
  3. Exploring Class-Based Views (CBV) Series #3 – Simplifying Form Processing with FormView
  4. Exploring Class-Based Views (CBV) Series #4 – How to Use ListView & DetailView
  5. Exploring Class-Based Views (CBV) Series #5 – Implementing CRUD with CreateView, UpdateView, and DeleteView
  6. Exploring Class-Based Views (CBV) Series #6 – How to Use TemplateView & RedirectView

“Maximize the potential of Django CBV with Mixins and build web applications that are easy to maintain and scalable!”