Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework. How Retrieve works

I am fairly new to Django rest framework and I had a couple questions that would really clear up a lot of stuff for me.

I was looking at docs for simple CRUD generic views like ListAPIView, Retrieve... etc.

For my list view I created it like this:

class CourseListApiView(ListAPIView):
    queryset = Course.objects.all()
    serializer_class = CourseListSerializer

Which makes sense because of the queryset returns Course.objects.all() so all the courses appear.

What I am not clear about is how the queryset in RetrieveApi works

class CourseRetrieveAPIView(RetrieveAPIView):
    queryset = Course.objects.all()
    serializer_class = CourseRetrieveSerializer

This is my retrieve view, it takes pk from my link and returns a corresponding course. What is unclear to me is why the queryset is Course.objects.all(), not a filtered query that gets the kwargs from the URL and filters my Courses. I tried it my way and got the same results, my view was:

class CourseRetrieveAPIView(RetrieveAPIView):
    serializer_class = CourseRetrieveSerializer

    def get_queryset(self):
        queryset = Course.objects.filter(pk=self.kwargs.get('pk'))
        return queryset 

This makes more sense since the queryset is Course.objects.filter(pk=self.kwargs.get('pk')) instead of Course.objects.all() which to me doesn't make sense since I am filtering my courses by the pk in the URL

Hope my question made sense. Leave a comment if you need any clarification. I know the answer will be pretty obvious but I am very new to the framework

like image 954
Nader Besada Avatar asked Jan 03 '23 07:01

Nader Besada


1 Answers

You will have to go through the codebase of rest_framework. A function named get_object uses two class variables named lookup_field and lookup_url_kwarg which have a default value of pk and None respectively.

Excerpt from the GenericAPIView in rest_framework/generics.py

def get_object(self):
    """
    Returns the object the view is displaying.

    You may want to override this if you need to provide non-standard
    queryset lookups.  Eg if objects are referenced using multiple
    keyword arguments in the url conf.
    """
    queryset = self.filter_queryset(self.get_queryset())

    # Perform the lookup filtering.
    lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

    assert lookup_url_kwarg in self.kwargs, (
        'Expected view %s to be called with a URL keyword argument '
        'named "%s". Fix your URL conf, or set the `.lookup_field` '
        'attribute on the view correctly.' %
        (self.__class__.__name__, lookup_url_kwarg)
    )

    filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
    obj = get_object_or_404(queryset, **filter_kwargs)

    # May raise a permission denied
    self.check_object_permissions(self.request, obj)

    return obj

As you can see the lookup_url_kwarg is set to be equal to lookup_field if nothing is specified. If you change this value to a field of your requirement then the filter in get_object_or_404 changes.

Now coming back to your issue, when you are specifying the filter manually with url kwargs you are not using the functionality provided by the RetrieveAPIView. Instead what you are doing is filtering out your result with pk from url kwargs in get_queryset and then sending that QuerySet result to get_object which will again do the same thing for you.

like image 105
Arpit Goyal Avatar answered Jan 16 '23 16:01

Arpit Goyal