Stupid.Fat.Cat
Stupid.Fat.Cat

Reputation: 11285

How do you paginate a viewset using a paginator class?

According to the documentation:

Pagination is only performed automatically if you're using the generic views or viewsets

But this doesn't seem to be the case. Here's what I have for my viewset:

views.py
class EntityViewSet(viewsets.ViewSet):
    def list(self, request):
        queryset = Entity.objects.all()
        serializer = EntitySerializer(queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        queryset = Entity.objects.all()
        entity = get_object_or_404(queryset, pk=pk)
        serializer = EntitySerializer(entity)
        return Response(serializer.data)

Here's my urls

entity_list = views.EntityViewSet.as_view({'get':'list'})
entity_detail = views.EntityViewSet.as_view({'get':'retrieve'})
    ...
    url(r'^entity/$', entity_list, name='entity-list'),
    url(r'^entity/(?P<pk>[0-9]+)/$', entity_detail, name='entity-detail'),
    ...

This is my pagination class

class PagePaginationWithTotalPages(pagination.PageNumberPagination):
    page_size = 30
    page_size_query_param = 'page_size'
    max_page_size = 1000

    def get_paginated_response(self, data):
        return Response({
            'next': self.get_next_link(),
            'previous': self.get_previous_link(),
            'count': self.page.paginator.count,
            'total_pages': self.page.paginator.num_pages,
            'results': data
        })

and I set it in settings.py

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated'
    ],
    'DEFAULT_PAGINATION_CLASS': 'myapp.pagination.PagePaginationWithTotalPages',
    'UNICODE_JSON': False,
}

Now while this works for ListAPIView it doesn't appear to work for my viewset. Is there a step that I'm missing?

For reference this works fine:

class EntitiesView(ListAPIView):
    serializer_class = EntitySerializer

    def get_queryset(self):
        parameters = get_request_params(self.request)
        qs = qs.filter(**parameters).distinct()
        return qs

EDIT:

Changing it to use ModelViewset appears to have done the trick

class EntityViewSet(viewsets.ModelViewSet):
    queryset = Entity.objects.all()
    serializer_class = EntitySerializer

    def list(self, request):
        queryset = self.queryset
        parameters = get_request_params(self.request)
        if 'ordering' in parameters:
            queryset = queryset.order_by(parameters['ordering'])
            del parameters['ordering']
        queryset = queryset.filter(**parameters).distinct()

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        entity = self.get_object()
        serializer = EntitySerializer(entity)
        return Response(serializer.data)

Upvotes: 1

Views: 2673

Answers (1)

Linovia
Linovia

Reputation: 20956

Pagination is only performed automatically if you're using the generic views or viewsets

You seem to have missed the next sentence from the documentation:

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.

As pointed by the documentation, the ListModelMixin will show you that you didn't call the paginate_queryset / get_paginated_response and thus did bypass the pagination as well as the filtering.

As pointed in the comments, you should consider ModelViewSet and define the required queryset to get it automatically included.

Upvotes: 1

Related Questions