Django: a class based view with mixins and dispatch method

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.


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.


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


It can be done with Django UserPassesTestMixin mixin or @user_passes_test decorator.

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):
    ...