【问题标题】:Pagination in Django-Rest-Framework using API-View使用 API-View 在 Django-Rest-Framework 中进行分页
【发布时间】:2015-05-18 05:48: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 进行分页的示例。

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

谢谢。

【问题讨论】:

    标签: django django-rest-framework django-pagination


    【解决方案1】:

    虽然 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)
    

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

    【讨论】:

    • 虽然您是对的,但接受的答案也是如此。 DRF 分页仅仅实现了 django 的内置分页。查看 rest_framework.pagination 的源代码。查找paginate_queryset 定义
    • 极好的添加黑麦。我没有看过源代码的那一部分,但是 django-rest-framework 分页的主干绝对是建立在 django.core.paginator 的标准 Django 分页器上的。我编辑了我之前的回复。
    • 一切都还好,但它应该返回页面而不是 cart_details,很容易修复:if page is not None: serializer = CartDetailSerializer(page, many=True) 和来自 DRF 的分页类 from rest_framework.settings import api_settingspagination_class = api_settings.DEFAULT_PAGINATION_CLASS
    • 我收到了这个错误。 'Settings' 对象没有属性 'DEFAULT_PAGINATION_CLASS' 我需要在 Setting 中做什么?
    • 这个答案应该可以帮助你stackoverflow.com/a/35525458/3045403
    【解决方案2】:

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

    Django Pagination in Views

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

    类似这样的:

    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)
    

    希望这会有所帮助。

    【讨论】:

    • 此解决方案在响应中没有下一页、上一页链接和计数。
    【解决方案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
    

    【讨论】:

      【解决方案4】:

      我使用的是 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-09-11
        • 2016-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多