Mixins in Django and Django Rest Framework
This is a general post about what mixins are. Mixins are a design pattern found in many languages. In Python, though the language does not support mixins natively,they are implemented using Python’s multiple inheritence model.
The mixins are different from inheritence. Inheritence is useful when you want to narrow a scope in the inherited class. Mixins fill in the gap when multiple orthogonal features have to be combined together. There is no narrowing of scope, hence traditional inheritence is not useful.
- mixins are classes that generally inherit from object (unless you are django core developer)
- mixins are narrow in scope as in they have single responsibility. They do one thing and do it really well.
- mixins provide plug-in functionality
- although mixins work through inheritence, they DONT create a subtyping relation. This means that mixins do not adhere to Liskoff principle from SOLID. Liskoff Principle: Instances of base class can be replaced with instances of derived classes and it will make sense. This statement does not hold for mixins. That’s why we say that mixins are not creating a subtyping relation.
- mixins are not meant to be extended :
class SubMixin(BaseMixin): DON’T DO THIS. Although Django core library is littered with things like
class PermissionRequiredMixin(AccessMixin):, make it a point to not do this in code you write. Python multiple inheritence makes it a nightmare to figureout method resolution order and all sorts of subtle issues might be caused. If there are loops in method resolution order, Python will give a compilation error. If you accidentally wrote,
class ComposedView(AccessMixin, PermissionRequiredMixin, BaseView)might cause MRO loops and hence compilation error because order matters.
- mixins are not meant to be instantiated
Mixins are really good at doing modular designs. Since Python supports multiple inheritence, this is totally fine:
class ComposedView(Mixin1, BaseView)
Read this as Mixin1 extends BaseView which is then extended by ComposedView.
class ComposedView(Mixin1,Mixin2, BaseView)
ORDER MATTERS. Keep the BaseView at the root of inheritance tree and each of the other mixins extend or add ONE specific part of the functionality. More specifically, Mixin2 will add to whatever Mixin1 and BaseView have added (can even overwrite if not careful). Take precaution that when chaining mixins, different mixins don’t end up editing the same base state or else they will all overwrite eachother’s stuff. This is a care that is paramount when using mixins. YOU NEED TO BE AWARE OF EXACTLY WHAT EACH MIXIN ADDS SO THAT ONE MIXIN DOES NOT OVERWRITE ANOTHER MIXIN’S MODIFICATIONS.
from django.core.exceptions import PermissionDenied class LoginRequiredMixin(object): """Verify that the current user is authenticated.""" def dispatch(self, request, *args, **kwargs): if not request.user.is_authenticated(): raise PermissionDenied return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
Here we we have a mixin to check if a user is logged in. This mixin is written with the assumption that it will be extending a baseview which will have a dispatch method with exactly that signature. We choose to extend the dispatch method as that is one of the first methods hit in the request-response lifecycle.
Notice the super method at the end. In the scenario that the user is genuinely logged in, this super method launches the dispatch of the baseview that got extended.
Another more practical example. Lets take
TemplateView whose sole purpose is to show a given html template. Just by adding the mixin, this TemplateView can only be accessed by logged in user.
from django.views.generic import TemplateView class AboutView(LoginRequiredMixin, TemplateView): template_name = "about.html"
The AboutView inherits from both LoginRequiredMixin and TemplateView.
Now if you wanted to add this login requirement to DetailView instead of a TemplateView,
class AboutView(LoginRequiredMixin, TemplateView): becomes
class AboutView(LoginRequiredMixin, DetailView):
That’s it. Your DetailView will not need anymore code change to implement the login functionality.
Writing mixins for Django Class Based Views
Mixins are a natural fit for class based views just like decorators are a natural fit for function based views.
To write mixins for a class based view, it is often good to know a little about the view lifecycle. Lets go through some lifecycle methods in order:
- mixins override dispatch if they need to check if user if logged in, has permission. This is one of the first methods called and view determines which request method (get, post etc) is used.
- mixins override get_context_data() to add new data to context. This method passes keyword arguments to the template context. So this is a way to add extra context to the views.
- mixins override get_template_names(). This is a method called by
render_to_responseand this method lists all template names.
render_to_responseis the original version for which there are many shortcuts like
So overriding get_template_names() in a mixin gives more flexibility in how to identify template names, in case your Django project has a specific way.
A VERY NICE PLACE TO SEE ALL DJANGO CLASS BASED VIEWS: http://ccbv.co.uk/
django-braces is a collection of frequently used django mixins and they are divided in three categories.
- Access Mixins
- Form Mixins
- Other Mixins
Very common in function based views are login_required(), user_passes_test(), permission_required()
In class based views, you get their equivalent mixins. LoginRequiredMixin, UserPassesTestMixin, PermissionRequiredMixin
Anti patterns in mixin use in Django
Beware of incompatible mixins, chaining too many mixins, overdoing mixins otherwise it will it will be a nightmare to figure out the execution flow. Same as we advise against 100 lines view functions and tons of decorators on functions, we also advise against scenarios like if somethin requires 6 mixins to work, you are probably doing something wrong and placing too much logic into your view layer.
Chaining about 3 mixing is OK. Like chaining PermissionRequiredMixin and LoginRequiredMixin is very common because they both work with dispatch but do not interfere with eachother. One deals with authorization, another with authentication. Maybe you can add another mixin to work on the get_context_data(). But the more mixins you add, the more logic gets added to view layer and it becomes harder to understand the logic and execution flow. 4 mixins is considered absolute pushing it.