Django DetailView - How to change the get_object to check a field

Alex Haines picture Alex Haines · Mar 7, 2017 · Viewed 18.5k times · Source

So I want to make a DetailView that shows a photo by itself and its associated information. However, I want to have it make sure the user has permission to access the photo as well.

This is the urls.py for the view

url(r'^photo/(?P<slug>[\-\d\w]+)/$', views.PhotoDetail.as_view(), name='photo'),

This is the views.py

class PhotoDetail(DetailView):
    template_name = 'otologue/photo_detail.html'

    def get_object(self, queryset=None):
        slug = self.get_slug_field()
        print(slug)
        object_instance = Photo.objects.filter(slug=slug)
        print(object_instance)
        object_user = object_instance.photoextended.user
        user = get_object_or_404(User, username=self.request.user)  # Get the user in the view

        if object_user != user:  # See if the object_user is the same as the user
            return HttpResponseForbidden('Permission Error')

        else:
            return object_instance

As you can see I try to get_slug_field() but when I print it it only says 'slug' when the slug should say the objects slug which is in the url. From this the object_instance doesn't have any object and then why I try to get the user from it, it can't find any data.

Answer

Alasdair picture Alasdair · Mar 7, 2017

The DetailView's get_object method already knows how to fetch an object by the slug. There's no need to duplicate this code, just call super().

Then you can compare the user to self.request.user directly - there's no need to refetch the user from the database with get_object_or_404.

Finally, you can't return a response from get_object, the method is meant to return the object. You can however raise an exception like Http404.

from django.http import Http404

class PhotoDetail(DetailView):

    def get_object(self, queryset=None):
        obj = super(PhotoDetail, self).get_object(queryset=queryset)
        if obj.user != obj.photoextended.user:
            raise Http404()
        return obj

A common approach in class based views is to override get_queryset and filter by user. The get_object method will use this queryset when fetching the object. If the object is not in the queryset, then the user will get a 404 error.

class PhotoDetail(DetailView):

    def get_queryset(self):
        queryset = super(PhotoDetail, self).get_queryset()
        return queryset.filter(photoextended__user=self.request.user)