Django: a class based view with mixins and dispatch method
Normally, I use a
dispatch method of a class based view to set some initial variables or add some logic based on user's permissions.
from django.views.generic import FormView from braces.views import LoginRequiredMixin class GenerateReportView(LoginRequiredMixin, FormView): template_name = 'reporting/reporting_form.html' form_class = ReportForm def get_form(self, form_class): form = form_class(**self.get_form_kwargs()) if not self.request.user.is_superuser: form.fields['report_type'].choices = [ choice for choice in form.fields['report_type'].choices if choice != INVOICE_REPORT ] return form
It works as expected: when an anonymous user visits a pages, the
dispatch method of LoginRequiredMixin is called, and then redirects the user to the login page.
But if I want to add some permissions for this view or set some initial variables, for example,
class GenerateReportView(LoginRequiredMixin, FormView): def dispatch(self, *args, **kwargs): if not ( self.request.user.is_superuser or self.request.user.is_manager ): raise Http404 return super(GenerateReportView, self).dispatch(*args, **kwargs)
in some cases it doesn't work, because
dispatch methods of the mixins, that the view inherits, haven't been called yet. So, for example, to be able to ask for user's permissions, I have to repeat the validation from
class GenerateReportView(LoginRequiredMixin, FormView): def dispatch(self, *args, **kwargs): if self.request.user.is_authenticated() and not ( self.request.user.is_superuser or self.request.user.is_manager ): raise Http404 return super(GenerateReportView, self).dispatch(*args, **kwargs)
This example is simple, but sometimes there are some more complicated logic in a mixin: it checks for permissions, makes some calculations and stores it in a class attribute, etc.
For now I solve it by copying some code from the mixin (like in the example above) or by copying the code from the
dispatch method of the view to another mixin and inheriting it after the first one to execute them in order (which is not that pretty, because this new mixin is used only by one view).
Is there any proper way so solve these kind of problems?
I would write custom class, which check all permissions
from django.views.generic import FormView from braces.views import AccessMixin class SuperOrManagerPermissionsMixin(AccessMixin): def dispatch(self, request, *args, **kwargs): if not request.user.is_authenticated(): return self.handle_no_permission(request) if self.user_has_permissions(request): return super(SuperOrManagerPermissionsMixin, self).dispatch( request, *args, **kwargs) raise Http404 #or return self.handle_no_permission def user_has_permissions(self, request): return self.request.user.is_superuser or self.request.user.is_manager # a bit simplyfied, but with the same redirect for anonymous and logged users # without permissions class SuperOrManagerPermissionsMixin(AccessMixin): def dispatch(self, request, *args, **kwargs): if self.user_has_permissions(request): return super(SuperOrManagerPermissionsMixin, self).dispatch( request, *args, **kwargs) else: return self.handle_no_permission(request) def user_has_permissions(self, request): return request.user.is_authenticated() and (self.request.user.is_superuser or self.request.user.is_manager) class GenerateReportView(SuperOrManagerPermissionsMixin, FormView): #Remove next two lines, don't need it def dispatch(self, *args, **kwargs): #or put some logic here return super(GenerateReportView, self).dispatch(*args, **kwargs)
And implementation of class GenerateReportView(SuperOrManagerPermissionsMixin, FormView) does not require overriding dispatch method
If you use multiple inheritance and one of the parent classes need some improvement, it's good to improve it first. It keeps code cleaner.
Introduction to class-based views | Django documentation, Sharing Class-Based Views attributes across several view methods ( get() , post() ) class UsernameStartsWithAMixin: def dispatch(self, request, *args, **kwargs): This allows inherited classes and mixins to do instance variable setup and� Django’s built-in class-based views provide a lot of functionality, but some of it you may want to use separately. For instance, you may want to write a view that renders a template to make the HTTP response, but you can’t use TemplateView; perhaps you need to render a template only on POST, with GET doing something else entirely.
this is an old post but other people might come across so here is my proposed solution.
When you say
"[...]I want to add some permissions for this view or set some initial variables, for example[...]"
Instead of setting those initial variables in the dispatch method of your view, you could write a separated method for setting up those variables, and then call that method in your get (and post if needed) method. They are called after dispatch, so setting up your initial variables wont clash with the dispatch in your mixins. So override the method
def set_initial_variables(): self.hey = something return def get(blablabla): self.set_initial_variables() return super(blabla, self).get(blabla)
This probably is cleaner than copying and pasting the code of your mixin in your view's dispatch.
#29750 (Add a pre-dispatch() hook for class-based views) – Django, All other class-based views inherit from this base class. Initializes view instance attributes: self.request , self.args , and self.kwargs prior to dispatch() . Overriding this method allows mixins to setup instance attributes for reuse in child classes. Using mixins¶. Mixins are a form of multiple inheritance where behaviors and attributes of multiple parent classes can be combined. For example, in the generic class-based views there is a mixin called TemplateResponseMixin whose primary purpose is to define the method render_to_response().
For the example you gave, I would use
UserPassesTestMixin from django-braces.
class GenerateReportView(UserPassesTestMixin, FormView): def test_func(self, user): return user.is_superuser or user.is_manager
If that isn't suitable for your more complicated logic, then creating a separate mixin sounds like an OK approach, as it encapsulates the complicated logic nicely.
EDIT As of django 1.9, the UserPassesTestMixin is now included in django: https://docs.djangoproject.com/en/1.11/topics/auth/default/#django.contrib.auth.mixins.UserPassesTestMixin
Base views | Django documentation, from django.views.decorators.cache import cache_page, never_cache You shouldn't override dispatch , but the class method as_view(). See the docs, which � By starting with something familiar, we can refactor the code into custom class-based views. The result is a base view class with mixins and generics, similar in design to the classes used by Django and Django-Rest-Framework. What This Tutorial Isn’t. We aren’t building a perfect reproduction of Django’s class based views.
Example of UserPassesTestMixin
from django.contrib.auth.mixins import UserPassesTestMixin class SuperUserOrManagerRequiredMixin(UserPassesTestMixin): def test_func(self): if self.request.user.is_superuser or self.request.user.is_manager: return True return False class MyView(LoginRequiredMixin, SuperUserOrManagerRequiredMixin, View): ...
Class-based views | Django documentation, def dispatch(self, *args, **kwargs): from django.views.generic.detail import DetailView You shouldn't override dispatch , but the class method as_view(). See� Django's class-based views provide a object-oriented (OO) way of organizing your view code. Most Django tutorials and training material start developers off with the simple style of function-based views (which were available in Django long before class-based views).
Django class based view mixins, For example, from django.views.generic import FormView from braces.views import LoginRequiredMixin class GenerateReportView(LoginRequiredMixin,� I was reading today the book Two Scoops of Django (affiliate link) by Audrey and Daniel Roy Greenfeld and found some interesting tips about using class-based views with mixins. Some general rules to use mixins to compose your own view classes, as suggested by Kenneth Love, which I grabbed from the above mentioned book:
Django: a class based view with mixins and dispatch method, dispatch(), it will call TemplateView's dispatch() method. How I wish I had known this when I was learning about CBVs! View/BaseView/Mixin. As� Django class based view provides a class instance method as_view() which serves as an entry point for any generic CBV. Django URL resolver expects to send the request passed through it to a callable(a function). The as_view() class instance method in each generic view creates a hook for calling the class just like a method.
Django Class-Based Generic Views: tips for beginners (or things I , Django class based view provides a class instance method as_view() The as_view() method then calls the dispatch() method which in turn calls It is worth noting that Extending a class view with multiple Mixins can lead to� A view can be either function or a class-based view. CBVs inherit the method as_view() which uses a dispatch() method to call the appropriate method Django provides a set of views and mixins
- first call
super(GenerateReportView, self).dispatch(*args, **kwargs). then do the rest of the job inside the dispatch you are overriding
- @MihaiZamfir It won't work because, for example,
HttpResponseRedirectobject, so if we store it in a variable, it won't make a redirect to the login view until we return this variable at the end of
- but you can check if the response is a redirect, then continue with the dispatch
- The issue is that upstream processing of mixins may result in valid redirect. E.g. There is inheritance hierarchy like (LoginRequiredMixin, CustomViewWhichRedirectsSometimes), now, if dispatch of login required returns redirect, we need to somehow judge if it's because user is not authorized or if perfectly expected redirect from CustomViewWhichRedirectsSometimes.