19

我目前有一个 API 视图设置如下:

class CartView(APIView):
    authentication_classes = [SessionAuthentication, TokenAuthentication]
    permission_classes = [IsAuthenticated, ]
    api_view = ['GET', 'POST']

    def get(self, request, format=None):
        try:
            cart = request.user.cart
        except Cart.DoesNotExist:
            cart = Cart.objects.create(user=request.user)
        cart_details = cart.cart_details.all()
        serializer = CartDetailSerializer(cart_details, many=True, fields=['id', 'item', 'quantity', 'product_type'])
        return Response(serializer.data)

CartDetailSerializer是一个普通的 ModelSerializer。

我想对这个 API 进行分页。但是,在 DRF 的文档中,我发现了这一点:

如果您使用的是常规 APIView,则需要自己调用分页 API 以确保返回分页响应。

没有提供关于如何对常规 APIView API 进行分页的示例。

任何人都可以发布一个我可以在上述场景中使用的示例。

谢谢。

4

4 回答 4

24

虽然 rayy 提到的方式是可能的,但 django-rest-framework 可以通过一些额外的功能在内部处理这个问题,使使用 API 变得更加容易。(*note django-rest-framework 的分页是从 django.core.paginator 的 Django 分页器构建的)

在您引用的内容之后是解决此问题的关键信息:

仅当您使用通用视图或视图集时,才会自动执行分页。如果您使用的是常规 APIView,则需要自己调用分页 API 以确保返回分页响应。有关示例,请参阅 mixins.ListMixin 和 generics.GenericAPIView 类的源代码。

对那里所说的内容进行轻微修正:查看 ListModelMixin。

如果你去这两个链接,你可以看到上述文件的源代码: generics.py mixins.py

您需要做的是包含类似以下内容以使分页在 APIView 中工作(**注意:此代码未经测试,但想法是正确的。还有一种更好的编写方法,而不必包含代码在每一种观点中,但我将把它留给你,以使我的回答简短易懂):

from __future__ import absolute_import
# if this is where you store your django-rest-framework settings
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.response import Response

from .models import Cart 

class CartView(APIView):
    pagination_class = settings.DEFAULT_PAGINATION_CLASS

    def get(self, request, format=None):
        #assuming every other field in the model has a default value    
        cart = Cart.objects.get_or_create(user=request.user)

        #for a clear example
        cart_details = Cart.objects.all()

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

        serializer = CartDetailSerializer(cart_details, many=True)
        return Response(serializer.data)

    @property
    def paginator(self):
        """
        The paginator instance associated with the view, or `None`.
        """
        if not hasattr(self, '_paginator'):
            if self.pagination_class is None:
                self._paginator = None
            else:
                self._paginator = self.pagination_class()
        return self._paginator

    def paginate_queryset(self, queryset):
        """
        Return a single page of results, or `None` if pagination is disabled.
        """
        if self.paginator is None:
            return None
        return self.paginator.paginate_queryset(queryset, self.request, view=self)

    def get_paginated_response(self, data):
        """
        Return a paginated style `Response` object for the given output data.
        """
        assert self.paginator is not None
        return self.paginator.get_paginated_response(data)

我希望这对你和其他看到这篇文章的人有更多帮助。

于 2015-07-14T08:09:28.793 回答
20

使用常规 APIView 时,需要使用 Django 自带的 Paginator 类。

视图中的 Django 分页

在您的情况下,您可以在将查询集发送到序列化程序之前对其进行分页。

像这样的东西:

def get(self, request, format=None):
    try:
        cart = request.user.cart
    except Cart.DoesNotExist:
        cart = Cart.objects.create(user=request.user)
    cart_details = cart.cart_details.all()

    paginator = Paginator(cart_details, 10)
    page = request.GET.get('page')

    try:
        cart_details = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        cart_details = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        cart_details = paginator.page(paginator.num_pages)
    serializer = CartDetailSerializer(cart_details, many=True, fields=['id', 'item', 'quantity', 'product_type'])
    return Response(serializer.data)

希望这可以帮助。

于 2015-03-16T08:51:56.933 回答
3

我更喜欢扩展 Paginator 类,它的外观如下:

from rest_framework import status
from rest_framework.exceptions import NotFound as NotFoundError
from rest_framework.pagination import PageNumberPagination # Any other type works as well
from rest_framework.response import Response
from rest_framework.views import APIView

class CustomPaginator(PageNumberPagination):
    page_size = 10 # Number of objects to return in one page

    def generate_response(self, query_set, serializer_obj, request):
        try:
            page_data = self.paginate_queryset(query_set, request)
        except NotFoundError:
            return Response({"error": "No results found for the requested page"}, status=status.HTTP_400_BAD_REQUEST)

        serialized_page = serializer_obj(page_data, many=True)
        return self.get_paginated_response(serialized_page.data)

class CartView(APIView):

    def get(self, request, format=None):
        cart_details = Cart.objects.filter(user=request.user) # or any other query
        paginator = CustomPaginator()
        response = paginator.generate_response(cart_details, CartDetailSerializer, request)
        return response
于 2018-11-21T05:46:36.380 回答
1

我正在使用 DRF 3.6.2 版。您不需要编写太多代码。只需使用这个简单的步骤。

 class ProductPagination(PageNumberPagination):
        page_size = 5

    class product_api(generics.ListCreateAPIView):    
            queryset = Products.objects.all()
            serializer_class = product_serilizer
            pagination_class = ProductPagination

如果你想通过get方法来搜索功能,你可以写下面的代码

class ProductPagination(PageNumberPagination):
        page_size = 5

class product_api(generics.ListCreateAPIView):
    queryset = Products.objects.all()
    serializer_class = product_serilizer
    pagination_class = SearchProductPagination    

    def get_queryset(self):
        qs = super(product_search_api,self).get_queryset()
        searched_product = self.request.query_params.get('searched_product',None)
        if search:
            qs = Products.objects.filter(Q(product_name__icontains= searched_product))
        return qs
于 2017-05-03T09:51:36.207 回答