【问题标题】:Django Rest Framework: turn on pagination on a ViewSet (like ModelViewSet pagination)Django Rest Framework:在 ViewSet 上打开分页(如 ModelViewSet 分页)
【发布时间】:2015-10-25 11:33:54
【问题描述】:

我有一个这样的 ViewSet 来列出用户的数据:

class Foo(viewsets.ViewSet):

    def list(self, request):
        queryset = User.objects.all()
        serializer = UserSerializer(queryset, many=True)
        return Response(serializer.data)

我想像 ModelViewSet 的默认分页一样打开分页:

{
    "count": 55,
    "next": "http://myUrl/?page=2",
    "previous": null,
    "results": [{...},{...},...,{...}]
}

The official doc 说:

只有在您使用通用视图或视图集时才会自动执行分页

...但是我的结果集根本没有分页。如何分页?

【问题讨论】:

  • 这是来自类似 Stack Overflow 问题的非常 simple answer

标签: python django django-rest-framework


【解决方案1】:

这种方式也适用于 Django Rest apiViews Api 的

from rest_framework.pagination import PageNumberPagination
class FooList(APIView):

    page_size = 10

    def get(self, request):
        foo = Foo.objects.all()
        paginator = PageNumberPagination()
        paginator.page_size = page_size
        result_page = paginator.paginate_queryset(foo, request)
        serializer = FooSerializer(result_page, many=True)
        return paginator.get_paginated_response(serializer.data)

【讨论】:

    【解决方案2】:

    DRF中使用viewsetslist进行分页。

    这里我处理了一个异常如果 pageempty 它将显示 empty 记录。

    在设置定义页面大小时,这个页面大小是全局的并且被paginator_queryset在视图中使用。

    REST_FRAMEWORK = { 'PAGE_SIZE': 10, }

    view.py 中:

    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_200_OK,
                    "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
            })
    

    注意:如果您不使用Order_by,它将显示exception,因为此列表 给出无序列表。

    【讨论】:

    • 你也可以访问stackoverflow.com/a/46173281/8231158我给出了两个答案一个敌人视图集,另一个是API视图。
    • queryset = Sitting.objects.all() ,获取所有记录然后根据它进行分页是不是错了?如果有数百万条记录,会不会影响性能?
    • Sitting.objects.all() 这是一个示例@Sandhu 根据您的要求您可以过滤记录,您可以根据您的要求编写自己的查询
    • 您应该返回带有空列表的200 OK,因为服务中确实存在 URL 和集合,但没有一个与查询参数匹配。检查stackoverflow.com/questions/13366730
    • 感谢@JPVentura,已更新
    【解决方案3】:

    如果您想为特定的ViewSet 分页,但不需要自定义页面大小,则this answer 的一个稍微简单的变体:

    REST_FRAMEWORK = {
        'PAGE_SIZE': 100
    }
    
    class FooViewSet(viewsets.ModelViewSet):
        pagination_class = PageNumberPagination
    

    【讨论】:

      【解决方案4】:

      对于那些使用 DRF 3.1 或更高版本的用户,他们正在更改处理分页的默认方式。详情请见http://www.django-rest-framework.org/topics/3.1-announcement/

      现在,如果您想为 ModelViewSet 启用分页,您可以通过在 settings.py 文件中进行设置来全局执行此操作:

      REST_FRAMEWORK = {
          'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
          'PAGE_SIZE': 100
      }
      

      或者,如果您只希望它用于一个 ModelViewSet,您可以手动为该视图集设置 pagination_class。

      from rest_framework.pagination import PageNumberPagination
      
      class StandardResultsSetPagination(PageNumberPagination):
          page_size = 100
          page_size_query_param = 'page_size'
          max_page_size = 1000
      
      class FooViewSet(viewsets.ModelViewSet):
          pagination_class = StandardResultsSetPagination
      

      这还允许您调整仅针对该视图集处理分页的方式。

      DRF 3.1 还引入了可以使用的新类型的默认分页方案,例如 LimitOffset 和 Cursor。

      【讨论】:

      • 如果您使用 PageNumberPagination 请求 page1,然后将一个新项目添加到列表中,然后您再请求 page2,则您刚刚在 page1 中获得的最后一个项目将再次显示为第一个page2 中的项目(连续显示 2 次)。 CursorPagination 是比 PageNumberPagination 更推荐的方式。 CursorPagination 保持对对象的引用,不必为每个页面计算内容。实施见stackoverflow.com/a/47657610/5881884
      【解决方案5】:

      尝试提供类变量

      paginate_by = 10 #This will paginate by 10 results per page.
      

      创建一个自定义 ViewSet,它只执行 list 操作,作为您目前的情况。

      class ListModelViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
          pass
      

      现在用这个定制的视图继承你的类Foo

      class Foo(ListModelViewSet):
      
          paginate_by = 10
      
          def list(self, request):
              queryset = User.objects.all()
              serializer = UserSerializer(queryset, many=True)
              return Response(serializer.data)
      

      这应该可以帮助您使分页正常工作。

      【讨论】:

      • 我添加了paginate_by = 10,但它不起作用。如果我将class Foo(viewsets.ViewSet): 更改为class Foo(generics.ListCreateAPIView):,我会收到此错误:as_view() 仅采用 1 个参数(给定 3 个)
      • 您是否将任何参数传递给 .as_view()?其次,你为什么不使用ModelViewSet
      • 不,与 .as_views() 无关。我没有使用ModelViewSet,因为我需要编写一些逻辑来过滤我的queryset 的子集并将一些数据添加到与用户模型无关的结果集中(我的问题代码中省略了该逻辑)跨度>
      • 还是不行 :( PS:你为什么用MongoGenericViewSet?我用viewsets.GenericViewSet代替...
      【解决方案6】:

      只有在您使用泛型时才会自动执行分页 视图或视图集

      第一个障碍是将文档翻译成英文。他们想要传达的是你想要一个通用的视图集。通用视图集从 generic ApiViews 扩展而来,它具有用于对查询集和响应进行分页的额外类方法。

      另外,您提供了自己的list 方法,但默认分页过程实际上由mixin 处理:

      class ListModelMixin(object):
          """
          List a queryset.
          """
          def list(self, request, *args, **kwargs):
              queryset = self.filter_queryset(self.get_queryset())
      
              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)
      

      简单的解决方案,使用框架代码:

      class Foo(mixins.ListModelMixin, viewsets.GenericViewSet):
          queryset = User.objects.all()
          serializer = UserSerializer
      

      更复杂的解决方案是,如果您需要自定义 list 方法,那么您应该按照您认为合适的方式编写它,但采用上述混合代码 sn-p 的样式。

      【讨论】:

        猜你喜欢
        • 2016-08-01
        • 2017-11-06
        • 1970-01-01
        • 2016-09-14
        • 1970-01-01
        • 2016-06-08
        • 2016-06-15
        • 2017-04-09
        • 2015-10-22
        相关资源
        最近更新 更多