djangodjango-permissionsdjango-guardian

Make PermissionRequiredMixin also check for object level permission


I am using Django Guardian to have object-level permission along with global permissions. Some of the users have a group with global permissions, and some have object-level permissions. With this, I seem to need to modify the PermissionRequiredMixin to check also for the object-level permission.

views.py

class MainPageView(PermissionRequiredMixin, TemplateView):
    permission_required = "app.view_mainpage"
    template_name = "web/mainpage.html"

This one works if the user has global permission but if the user is under a group with object-level permission, it won't. With guardian, to check for object-level permission, you also have to pass the object instance.

example:

self.request.user.has_perm('view_mainpage', obj)

And on PermissionRequiredMixin, the checking goes only like this, self.request.user.has_perms(perms)

So if the user has a group with permission of view_mainpage of a specific object, how can I check it too? By the way, all my permissions are of the same content_type. Is there any way I can execute this? Like I have to pass the object instance to PermissionRequiredMixin if the user is under an object-level group and None if the user is under a global group.


Solution

  • You can implement such mixin yourself with:

    from django.core.exceptions import PermissionDenied
    
    class ObjectPermissionRequiredMixin:
        object_permission_required = None
        object_permission_denied_message = None
    
        def has_object_permission(self, obj):
            return self.request.user.has_perm(self.object_permission_required, obj)
    
        def get_object_permission_denied_message(self):
            return self.object_permission_denied_message
    
        def handle_no_object_permission(self):
            raise PermissionDenied(self.get_object_permission_denied_message())
    
        def get_object(self, *args, **kwargs):
            obj = super().get_object(*args, **kwargs)
            if not self.has_object_permission(obj)
                return self.handle_no_object_permission()
            return obj

    Then you can mix this mixin into a view, for example:

    class MyUpdateView(ObjectPermissionRequiredMixin, UpdateView):
        model = MyModel
        object_permission_required = 'app.update_my_model'
        object_permission_denied_message = 'You can not edit this object'

    This will work for all Views that usee the SingleObjectMixin [Django-doc] such as a DetailView, UpdateView, and the DeleteView.