dabadaba
dabadaba

Reputation: 9522

Class-based views: where to check for permissions?

I am not very comfortable using class-based views but I am aware of their perks so I am forcing myself to start using them more often.

There's this view that receives a path param: manage/:id to manage a particular entity.

class MyView(TemplateView):
  template_name = '...'

  def get_context_data(self, **kwargs):
    context = super(MyView, self).get_context_data(**kwargs)
    context['entity'] = get_object_or_404(Entity, pk=self.args[0])
    return context

An Entity includes a list of authorized users to perform special actions. This view, MyView is one of those special actions.

I tried making a decorator for the view but it required finding the Entity first so I wasn't sure how to work that out.

Now, I have a check_permission(request, entity) function that checks if the current user is one of these authorized ones.

My question is where should I call this function in the class-based views like MyView which will be any of these views considered "special actions"?

Should I call it just from get_context_data()?

Upvotes: 6

Views: 5064

Answers (3)

yedpodtrzitko
yedpodtrzitko

Reputation: 9359

put it into dispatch(). It could look like this:

class MyView(TemplateView):
   template_name = '...'

   def dispatch(self, request, *args, **kwargs):
       entity = get_object_or_404(Entity, pk=args[0])
       if not check_permission(request, entity):
           raise Http404
       return super(MyView, self).dispatch(request, *args, **kwargs)

Upvotes: 9

denvaar
denvaar

Reputation: 2214

You can check permissions in the dispatch as yedpodtrzitko has said. I think it's also a good idea to throw it inside of a mixin that you can put on your views.

Here's an example:

from django.core.exceptions import PermissionDenied


class ViewPermissionsMixin(object):
    """Base class for all custom permission mixins to inherit from"""
    def has_permissions(self):
        return True 

    def dispatch(self, request, *args, **kwargs):
        if not self.has_permissions():
            raise PermissionDenied
        return super(ViewPermissionsMixin, self).dispatch(
            request, *args, **kwargs)

class MyCustomPermissionMixin(ViewPermissionsMixin):

    def has_permissions(self):
        # here you will have access to both
        # self.get_object() and self.request.user
        return self.request.user in self.get_object().special_list_of_people

Now you can throw MyCustomPermissionMixin on your view:

class MyView(MyCustomPermissionMixin, TemplateView):
    # ...

In your case, since you're using a TemplateView, you should also make a get_object() method that returns the object that you want to deal with. Template views don't have this method by default.

Finally, just want to say that you will love Django's class based views once you learn some more about how to use them.

Upvotes: 8

Ogre Codes
Ogre Codes

Reputation: 19621

Take a look at Django Braces, it's a solid set of mixins which are designed around permissions.

How specifically you deal with permissions depends largely on implementation. I've done it in dispatch() before which is the way Braces does it but if it's specific to an object or queryset, I'll do it in the actual get_object or get_queryset methods as part of a DetailView.

For example if you had a creator associated with an Entity, you could override get_object to check the current logged in user is the Entity's creator.

class EntityView(LoginRequiredMixin, DetailView):
    model = Thing

    def get_object(self, **kwargs):
        return Entity.objects.get_object_or_404(
            pk=kwargs['entity_id'], 
            creator=self.request.user
        )

Note: LoginRequiredMixin is a part of Braces. Very slick.

Upvotes: 1

Related Questions