How can i set pagination dynamically in Django Rest Framework?

Question:

I made an API endpoint using Django Rest Framework, it can be filtered according to two parameters. I’m trying to do the following: when there is no filter, it should retrieve x number of records, while when there is one or more filter, it needs to retrieve more records. So basically i need to change the pagination if there are filters.

Here is what i tried:

class My_View(viewsets.ModelViewSet):
    http_method_names = ['get']
    serializer_class = My_Serializer
    pagination_class = StandardResultsSetPagination

    def get_queryset(self):

        valid_filters = {
            'Name': 'Name',
            'Date': 'Unix__gte',
        }

        filters = {valid_filters[key]: value for key, value in self.request.query_params.items() if key in valid_filters.keys()}

        if len(filters) == 0:
            pagination_class = StandardResultsSetPagination
        else:
            pagination_class = LargeResultsSetPagination

        queryset = My_Model.objects.filter(**filters)

        return queryset

What this code what supposed to do is to set a standard pagination of 100 if there isn’t a filter, and 200 if there are filters.

This code doesn’t work, the pagination will always be set to StandardResultsSetPagination, and it doesn’t get changed. I think it’s because get_queryset is called after the pagination is set, so it can’t be set again later. Is there any way to do that?

Asked By: JayK23

||

Answers:

You will need to override the get_page_size method of your pagination class:

from rest_framework.pagination import LimitOffsetPagination

valid_filters = {'Name', 'Date'}
def _has_valid_filters(iterable):
    return not valid_filters.isdisjoint(iterable)

class MyPagination(LimitOffsetPagination):
    def get_page_size(self, request):
        if _has_valid_filters(request.query_params.items()):
            return 200
        else:
            return 100

class MyView(viewsets.ModelViewSet):
    pagination_class = MyPagination
    ...
Answered By: Lord Elrond

I know it’s solved but here is more dynamic way. create a function that return a PageNumberPagination subclass class with dynamic page_size (you can add other attrs too, to make them dynamic)

from rest_framework.pagination import PageNumberPagination


def customPagination(page_size):
    return type("SubClass", (PageNumberPagination,), {"page_size": page_size})


class MyViewA(viewsets.ModelViewSet):
    pagination_class = customPagination(page_size=20)
    ...

class MyViewB(viewsets.ModelViewSet):
    pagination_class = customPagination(page_size=50)
    ...
Answered By: Ali Aref