using pagination in serializer django

2019-09-02 17:32发布

问题:

I am working on a django project in which I am trying to fetch all the products having update_ts field greater than product_sync_ts from Product table from the server at a time, but since the number of products is large this requires a large amount of time to fetch all data.I am also returning the current time stamp along with the data.Now I want to paginate the results.

My Previous code :

Views.py

@api_view(['GET'])
def productlist(request, format=None):
    product_sync_ts = request.GET.get(
            'product_sync_ts', None)
    if product_sync_ts:
        product =Product.objects.filter(
            update_ts__gt=product_sync_ts)
        )
        serializer = SyncedProductSerializer(
            instance={'products': product})
        return response(serializer.data)
    else:
        content = {'details': "Bad Request"}
        raise APIException400(request, content)

serializer.py

class SyncedProductSerializer(serializers.Serializer):
    product_sync_ts = serializers.SerializerMethodField(
        method_name='get_current_ts'
    )
    products = ProductSerializer(many=True, read_only=True)

    class Meta:
        fields = ('products', 'product_sync_ts')

    def get_current_ts(self, obj):
        product_sync_ts = datetime.utcnow().isoformat(' ')
        return product_sync_ts




class ProductSerializer(serializers.ModelSerializer):
    image_url = serializers.SerializerMethodField(
        method_name='change_image_url'
    )

    class Meta:
        model = Product
        fields = (
            'id',
            'name',
            'unit',
            'price',
            'image_url'
        )

    def change_image_url(self, obj):
        return ''.join([
            CDN_URL, str(obj.image_url)
        ]) if str(obj.image_url) else None

How I tried modifying it for pagination :

Views.py

class PaginatedProductList(APIView):

    def get(self, request, format=None):
        product_sync_ts = request.GET.get(
            'product_sync_ts', None)
        if product_sync_ts:
            product = GrProduct.objects.filter(
                update_ts__gt=product_sync_ts
            )
            paginator = Paginator(product, 1000)
            page = request.GET.get('page')
            try:
                 product = paginator.page(page)
            except PageNotAnInteger:
                 product = paginator.page(1)
            except EmptyPage:
                 product = paginator.page(paginator.num_pages)

             serializer = SyncedProductSerializer(
                 instance={'products': product})
            return Response(serializer.data)
        else:
            content = {'details': "Bad Request"}
            raise APIException400(request, content)

This is working fine, I am getting paginated results,but the problem is that I'm not able to send the next and previous page information to the client. The client has no way to know what are the valid(non-empty) number of pages.

Can someone suggest me how can I modify my code in order to do so.

回答1:

Here is an example how to paginate results with an APIView class and PageNumberPagination pagination class :

from rest_framework.views import APIView
from rest_framework.pagination import PageNumberPagination

class ProductList(APIView, PageNumberPagination):
    # number of items per page by default
    page_size = 1000
    # max number of items per page
    max_page_size = 1000

    def get_queryset(self):
        product_sync_ts = self.request.GET.get('product_sync_ts', None)
        if product_sync_ts:
            products = GrProduct.objects.filter(update_ts__gt=product_sync_ts)
            return self.paginate_queryset(products, self.request)

        raise APIException400(request, {'details': "Bad Request"})

    def get(self, request):
        products = self.get_queryset()
        serializer = SyncedProductSerializer(instance={'products': products})
        return self.get_paginated_response(serializer.data)

Read DRF - PageNumberPagination to configure your pagination



回答2:

There is GenericAPIView which has get_paginated_response method which returns

return self.paginator.get_paginated_response(data)

So you just need to subclass this Mixin

from rest_framework.generics import GenericAPIView

class PaginatedProductList(GenericAPIView): # Fixed

    def get(self, request, format=None):
        product_sync_ts = request.GET.get(
            'product_sync_ts', None)
        if product_sync_ts:
            product = GrProduct.objects.filter(
                update_ts__gt=product_sync_ts
            )
            paginator = Paginator(product, 1000)
            page = request.GET.get('page')
            try:
                 product = paginator.page(page)
            except PageNotAnInteger:
                 product = paginator.page(1)
            except EmptyPage:
                 product = paginator.page(paginator.num_pages)

            serializer = SyncedProductSerializer(
                 instance={'products': product})
            return paginator.get_paginated_response(serializer.data) # <- here it is
        else:
            content = {'details': "Bad Request"}
            raise APIException400(request, content)