Can someone explain when/how to use Queryset modification vs Permissioning with Django Rest Framework?

Viewed 118

I am struggling to understand how permissioning in DRF is meant to work. Particularly when/why should a permission be used versus when the queryset should be filtered and the difference between has_object_permission() & has_permission() and finally, where does the serializer come in.

For example, with models:

class Patient(models.Model):
  user = models.OneToOneField(User, related_name='patient')

class Appointment(models.Model):
  patient = models.ForeignKey(Patient, related_name='appointment')

To ensure that patients can only see/change their own appointments, you might check in a permission:

class IsRelevantPatient(BasePermission):
    def has_object_permission(self, request, view, obj):
        if self.request.user.patient == obj.appointment.patient:
            return True
        else:
            return False

But, modifying the queryset also makes sense:

class AppointmentViewSet(ModelViewSet):
    ...
    def get_queryset(self):
        if self.request.user.is_authenticated:
            return Appointment.objects.filter(patient=self.request.user.patient)

What's confusing me is, why have both? Filtering the queryset does the job - a GET (retrieve and list) only returns that patient's appointments and, a POST or PATCH (create or update) only works for that patient's appointments.

Aside from this seemingly redundant permission - what is the difference between has_object_permission() & has_permission(), from my research, it sounds like has_permission() is for get:list and post:create whereas has_object_permission() is for get:retrieve and patch:update. But, I feel like that is probably an oversimplification.

Lastly - where does validation in the serializer come in? For example, rather than a permission to check if the user is allowed to patch:update an object, You can effectively check permissions by overriding the update() method of the serializer and checking there.

Apologies for the rambling post but I have read the docs and a few other question threads and am at the point where I am probably just confusing myself more. Would really appreciate a clear explanation.

Thanks very much.

1 Answers

First, difference between has_object_permission() and has_permission() :

  • has_permission() tells if the user has the permission to use the view or the viewset without dealing with any object in the database

  • has_object_permission() tells if the user has the permission to use the view or the viewset based on a specific object in the database.

    The important note thaw is that DRF wont perform the test itself in the case of object level permission, but you have to do it explicitly by calling check_object_permission() somewhere in your view (doc here).

    The second important note is that DRF will not filter the result of the query based on object permission. If you want the query to be filtered, then you have to do it yourself (by overriding get_queryset() like you did or using a filter backend), that's the difference.

The serializer has nothing to do with permission neither with filtering. It handles objects one by one, applying validation (not permission) on each field of each objects.

Related