Search code examples
pythondjango-rest-frameworkdjango-viewsdjango-rest-viewsets

Why does a retrieve request end up calling `get_queryset( )`?


The DRF helps you make a simple API that exposes an endpoint for listings of an object and the details for a specific object.

My viewset looks like this:

class UserViewSet(ModelViewSet):
  serializer_class = someSerializer
  queryset = User.objects.all()
  
  def get_queryset(self):
    if self.request.user.has_perm('some.custom_permission'):
      return (
        super()
         .get_queryset()
         .filter(<some_custom_filtering>)
      )
    return super().get_queryset().filter(id=self.request.user.id)

Say my API is accessible from '<some_domain>/api/' (after registering my router appropriately). To get a listing of all my users I will call
<some_domain>/api/users/
And for a specific user (Michael Jordan)
<some_domain>/api/users/23

Both endpoints work as expected.

Just wondering why the call to retrieve a particular user still ends up going through the get_queryset() function.

Internally, the ModelViewSet class inherits functionality from the mixins.RetrieveModelMixin and the mixins.ListModelMixin. And if one goes even deeper, at least in djangorestframework==3.12.4, the RetrieveModelMixin gets a specific object, serializes it and returns the serialized data. While the ListModelMixin, calls the get_queryset() function.

I know that overriding the get_object() function in my UserViewSet view will make sure my retrieve object calls end up there (calling get_object instead of get_queryset(), but in its absence I don't get how or why the get_queryset() function is involved.


Solution

  • Queryset is the way to bind view to a specific model.
    You can create entire api with retrieve, list, update, create and delete actions simply by extending a few mixins and specifying queryset and serializer class, without actual method implementation.

    Whatever you do, it eventualy ends up as ModelClass.objects.some_method. As @Brian said, get_object also uses get_queryset, and also filter_queryset so retrieve/delete will raise 404 if you use filter and object does not pass it even if it actually exists.