Mark Longair
Mark Longair

Reputation: 467181

How do you use pagination in a Django REST framework ViewSet subclass?

I have a simple subclass of viewsets.ViewSet which looks like:

from rest_framework import viewsets
from rest_framework.response import Response

from ..models import Entry, Sitting, Source, Venue
from .serializers import (
    SittingSerializer, SittingWithEntriesSerializer,
)

class SittingViewSet(viewsets.ViewSet):

    def list(self, request, version=None):
        queryset = Sitting.objects.order_by('id')
        serializer = SittingSerializer(
            queryset, many=True, context={'request': request}
        )
        return Response(serializer.data)

    def retrieve(self, request, pk=None, version=None):
        prefetch_qs = Entry.objects.select_related('speaker')
        queryset = Sitting.objects.order_by('id') \
            .prefetch_related(Prefetch('entry_set', queryset=prefetch_qs))
        sitting = get_object_or_404(queryset, pk=pk)
        serializer = SittingWithEntriesSerializer(
            sitting, context={'request': request}
        )
        return Response(serializer.data)

However, the list view isn't paginated, as it is if you use a subclass of ModelViewSet. The settings I'm using are:

# Django Rest Framework settings:
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': ('pombola.api.permissions.ReadOnly',),
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
    'PAGE_SIZE': 10,
}

The documentation suggests looking at the source code for the mixins.ListModelMixin and generics.GenericAPIView classes, but I can't easily see how to reapply what they do to paginate results to these ViewSet methods.

Could anyone suggest what the simplest way would be to change this example to get pagination for the list view?

Upvotes: 8

Views: 5963

Answers (5)

John Moutafis
John Moutafis

Reputation: 23134

Although this comes late as an answer, I wrote a Q&A style example for Django Rest Framework which enables non-generic viewsets to have pagination.

By default, only the viewsets.GenericViewSet has automatic pagination (if you enable pagination in your settings of course), because it inherits from generics.GenericAPIView.

That leaves you with 2 options:

  1. The easy way:

    mixins.ListModelMixin provides pagination, so you can simply declare your viewset as follows:

    class SittingViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    

    and you now have a list() method with pagination.

  2. The harder way: *

    You can follow the example given above and create a pagination method inside your viewset.
    The list() code must be derived from the source code of the mixins.ListModelMixin provided above.
    The paginator(), paginate_queryset() and get_paginated_response() methods can be copied from the Documentation or the source code of generics.GenericApiView


  • I didn't add code example for the second option, because it would be just a copy/paste of the above mentioned links. I have made a related answers in this post: Pagination not working in DRF APIView

Upvotes: 4

Yonas
Yonas

Reputation: 141

Simple solution. Let's use the auth model as an example.

from django.contrib.auth.models import User

from rest_framework.pagination import PageNumberPagination
from rest_framework import viewsets

from .serializer import UserSerializer


class UserViewSet(viewsets.ViewSet):
    def list(self, request):
        queryset = User.objects.all()
        pagination = PageNumberPagination()
        qs = pagination.paginate_queryset(queryset, request)
        serializer = UserSerializer(qs, many=True)
        return pagination.get_paginated_response(serializer.data)


Upvotes: 4

Vinay Kumar
Vinay Kumar

Reputation: 1307

**Pagination in DRF using APIView and if you don't have serializer class **

Here I have used the django paginator, it is very simple

In view

from rest_framework.views import APIView
class SittingView(APIView):

 """This is a API code so when at e.g. (url /sitting-record/?page=2) is entered it will show 10 records for page 2 and if page is empty it will show empty"""

  def get(self, request, format=None):

    """Here 10 is the page size, that means 10 record per page"""
    sitting = Sitting.objects.all().order_by('id')
    sitting_paginator = Paginator(sitting, 10)

    """this gets the page page from url and if not given it set default page is 1"""
    page = request.GET.get('page')
    page = 1 if not page else page 

    try:
        paginated_setting = sitting_paginator.page(page)
    except Exception as e:
        paginated_setting = []   

    """you can retrieve or iterate the records fields from paginator_setting and then send the data in response"""
    data = {
        "setting records" : paginated_setting,
    }

    return Response({
        "status" : status.HTTP_200_OK,
        "messgae" : "Sitting content",
        "data"  : data
        })

Upvotes: 1

Vinay Kumar
Vinay Kumar

Reputation: 1307

Pagination in DRF using viewsets and list

Here I have handled a exception If page is empty it will show empty records

In setting define the page size, this page size is global and it is used by paginator_queryset in view

REST_FRAMEWORK = { 'PAGE_SIZE': 10, }

In view

    from rest_framework import mixins, viewsets

    class SittingViewSet(viewsets.GenericViewSet,
        mixins.ListModelMixin):

        serializer_class = SittingSerializer
        queryset = Sitting.objects.all()
        serializer = serializer_class(queryset, many=True)

        def list(self, request, *args, **kwargs):
            queryset =self.filter_queryset(Sitting.objects.all().order_by('id'))

            page = request.GET.get('page')

            try: 
                page = self.paginate_queryset(queryset)
            except Exception as e:
                page = []
                data = page
                return Response({
                    "status": status.HTTP_404_NOT_FOUND,
                    "message": 'No more record.',
                    "data" : data
                    })

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

            # serializer = self.get_serializer(queryset, many=True)
            return Response({
                "status": status.HTTP_200_OK,
                "message": 'Sitting records.',
                "data" : data
            })

**> Note: If you not use Order_by it will show exception because this list

gives unordered list.**

Upvotes: 0

Matúš Bartko
Matúš Bartko

Reputation: 2457

You overrided the list method, so it doesnt paginate your data.
If you check ListModelMixins I think this might be your answer:

class SittingViewSet(
    viewsets.GenericViewSet,
    mixins.ListModelMixin,
    mixins.RetrieveModelMixin):

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(
            Sitting.objects.order_by('id')
        )

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = SittingSerializer(
                page, 
                many=True, 
                context={'request': request}
                )
            return self.get_paginated_response(serializer.data)

        serializer = SittingSerializer(
                queryset, 
                many=True, 
                context={'request': request}
                )
        return Response(serializer.data)

Upvotes: 5

Related Questions