Search code examples
djangodjango-rest-frameworkpaginationdjango-filter

django-filter and django custom pagination with ModelViewSet


I implemented a modelviewset with django-filter and django default pagination comnbined. Its working fine when I use either django-filter or django pagination. But when they are used simultaneously then I am getting duplicate results in response.

So whats the correct way to use pagination in django-filter with CBV?

class TableMetaView(ModelViewSet):
    """
    This will be used to create new tables.
    You require to add the table fields in json request and also the job request associated with that
    table.
    If you want to create new table then pass CREATE NEW TABLE
    In response you will get the table list along with the job request for each tables
    """

    serializer_class = TableMetaSerializer
    queryset = TableMeta.objects.all()
    renderer_classes = [JSONRenderer]
    filterset_fields = [
        "schema_name",
        "type",
        "status",
        "grouping__name",
        "dataset__name",
    ]
    ordering_fields = ["created_on", "modified_on"]
    ordering = ["-modified_on"]
    pagination_class = StandardResultsSetPagination

    permission_classes = [
        UserHasDatasetChangeAccess & IsTableEditable,
    ]

    def get_queryset(self):
        if getattr(self, "swagger_fake_view", False):
            # queryset just for schema generation metadata
            return TableMeta.objects.none()
        return TableMeta.objects.filter(
            dataset=get_object_or_404(DataSet, id=self.request.META.get(DATASET_ID, ""))
        )

Solution

  • After searching a lot, I managed to do some changes and code is now running fine with django-filters and pagination.

    custom_pagination.py

    from collections import OrderedDict
    
    from constance import config
    from rest_framework.pagination import (  # noqa
        CursorPagination,
        LimitOffsetPagination,
        PageNumberPagination,
    )
    from rest_framework.response import Response
    
    
    class MyPagination(PageNumberPagination):
        page_size = 5
        page_size_query_param = "page_size"
        max_page_size = 50
    
        def get_paginated_response(self, data):
            return Response(
                OrderedDict(
                    {
                        "next": self.get_next_link(),
                        "previous": self.get_previous_link(),
                        "results": data,
                    }
                )
            )
    

    views.py

    class TableMetaView(ModelViewSet):
        """
        This will be used to create new tables.
        You require to add the table fields in json request and also the job request associated with that
        table.
        If you want to create new table then pass CREATE NEW TABLE
        In response you will get the table list along with the job request for each tables
        """
    
        serializer_class = TableMetaSerializer
        queryset = TableMeta.objects.all()
        renderer_classes = [JSONRenderer]
        filterset_class = TableFilter
        ordering_fields = ["created_on", "modified_on"]
        ordering = ["-modified_on"]
        pagination_class = MyPagination
    
        permission_classes = [
            UserHasDatasetChangeAccess & IsTableEditable,
        ]
    
        def get_queryset(self):
            if getattr(self, "swagger_fake_view", False):
                # queryset just for schema generation metadata
                return TableMeta.objects.none()
            return TableMeta.objects.filter(
                dataset=get_object_or_404(DataSet, id=self.request.META.get(DATASET_ID, ""))
            )
    
        def list(self, request: Request, *args: Any, **kwargs: Any) -> Response:
            filtered_qs = self.filterset_class(request.GET, queryset=self.get_queryset()).qs
            page = self.paginate_queryset(queryset=filtered_qs)
            serializer = self.serializer_class(page, many=True)
            return self.get_paginated_response(serializer.data)
    

    custom_filter.py

    class TableFilter(django_filters.FilterSet):
        class Meta:
            model = TableMeta
            fields = {
                "name": ["exact", "icontains"],
                "schema_name": ["exact"],
                "type": ["exact"],
                "status": ["exact"],
                "grouping__name": ["exact"],
                "dataset__name": ["exact"],
            }
    

    Also you need to set default ordering

    models.py

    class TableMeta(models.Model):
      class Meta:
        ordering = ['-modified_on'] # list of fields here
    

    After this django-filters and pagination are working fine in ModelViewSet.