django-rest-framework [Advanced] Pagination on Non Generic Views/Viewsets


Example

This is an advanced subject, do not attempt without first understanding the other examples of this page.

As stated in the official Django Rest Framework on pagination:

Pagination is only performed automatically if you're using the generic views or viewsets. If you're using a regular APIView, you'll need to call into the pagination API yourself to ensure you return a paginated response. See the source code for the mixins.ListModelMixin and generics.GenericAPIView classes for an example.

But what if we want to use pagination on a non generic view/viewset?

Well let's go down the rabbit hole:

  1. First stop is the official Django Rest Framework's repository and specifically the django-rest-framework/rest_framework/generics.py. The specific line this link is pointing at, shows us how the developers of the framework deal with pagination in their generics.
    That is exactly what we are going to use to our view as well!

  2. Let's assume that we have a global pagination setup like the one shown in the introductory example of this page and lets assume as well that we have an APIView which we want to apply pagination to.

  3. Then on views.py:

    from django.conf import settings
    from rest_framework.views import APIView
    
    class MyView(APIView):
        queryset = OurModel.objects.all()
        serializer_class = OurModelSerializer
        pagination_class = settings.DEFAULT_PAGINATION_CLASS # cool trick right? :)
    
        # We need to override get method to achieve pagination
        def get(self, request):
            ...
            page = self.paginate_queryset(self.queryset)
            if page is not None:
                serializer = self.serializer_class(page, many=True)
                return self.get_paginated_response(serializer.data)
    
            ... Do other stuff needed (out of scope of pagination)
    
        # Now add the pagination handlers taken from 
        #  django-rest-framework/rest_framework/generics.py
    
        @property
        def paginator(self):
            """
            The paginator instance associated with the view, or `None`.
            """
             if not hasattr(self, '_paginator'):
                 if self.pagination_class is None:
                     self._paginator = None
                 else:
                     self._paginator = self.pagination_class()
             return self._paginator
    
         def paginate_queryset(self, queryset):
             """
             Return a single page of results, or `None` if pagination is disabled.
             """
             if self.paginator is None:
                 return None
             return self.paginator.paginate_queryset(queryset, self.request, view=self)
    
         def get_paginated_response(self, data):
             """
             Return a paginated style `Response` object for the given output data.
             """
             assert self.paginator is not None
             return self.paginator.get_paginated_response(data)
    

Now we have an APIView that handles pagination!