0

I have a class based view for a Problem object. I only want the author of the Problem to be able to view the Problem cbv. Other logged-in users should be redirected to a forbidden page.

I achieve this by checking the ownership in the get_template_name() method. But if I want to pass in context to the forbidden template, I also need to check ownership in the get_context_data() and make the appropriate context.

This works, but it seems like way too much semi-repetitive code, and just very non-Pythonic/Djangonic.

Can any suggest a nicer way to do this? It's an issue for many ClassBasedViews I created. I have both "Problem" objects and "Board" objects that I want to ensure the logged-in user == the author of the Problem or Board object. Almost seems like I could have some kind of Mixin or something.

Anyway, here is an example of my approach:

class ProblemStudyView(UpdateView):
    model = Problem
    fields = "__all__"

    def get_template_names(self):
        problem = self.get_object()
        if problem.author != self.request.user:
            # User should not see this entry
            context = {'original_author': problem.author, 'request_user': self.request.user}
            template_name = "board/forbidden.html"
            return template_name
        else:
            # This user is OK
            template_name = "board/study.html"
            return template_name

    def get_context_data(self, **kwargs):
        context = super(ProblemStudyView, self).get_context_data(**kwargs)

        problem = self.get_object()

        # Do the permission check a second time to setup correct context
        if problem.author != self.request.user:
            context = {'original_author': problem.author, 'request_user': self.request.user}
            return context
        else:
            # User is ok; proceed as normal           
            return context
user3556757
  • 3,469
  • 4
  • 30
  • 70

1 Answers1

0

You could use the PermissionRequiredMixin and a custom Permission.

I'd do something like this:

from django.contrib.auth.mixins import PermissionRequiredMixin


class ProblemStudyView(PermissionRequiredMixin, UpdateView):

    model = Problem
    fields = "__all__"
    permission_denied_message = 'YOURMESSAGEHERE'
    raise_exception = True
    template_name = 'board/study.html'

    def dispatch(self, request, *args, **kwargs):
        self.request = request  # needed for has_permission
        self.kwargs = kwargs  # needed for get_object
        return super().dispatch(request, *args, **kwargs)

    def has_permission(self):
        problem = self.get_object()
        return problem.author == self.request.user

Since a PermissionDeniedException is raised, you can use (or alter) the standard Django http forbidden view: https://docs.djangoproject.com/en/1.11/ref/views/#the-403-http-forbidden-view

Pablo Vergés
  • 319
  • 2
  • 8