Django Rest Framework:在 ViewSet 上打开分页(如 ModelViewSet 分页)

Posted

技术标签:

【中文标题】Django Rest Framework:在 ViewSet 上打开分页(如 ModelViewSet 分页)【英文标题】:Django Rest Framework: turn on pagination on a ViewSet (like ModelViewSet pagination) 【发布时间】: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。 【参考方案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,因为此列表 给出无序列表。

【讨论】:

你也可以访问***.com/a/46173281/8231158我给出了两个答案一个敌人视图集,另一个是API视图。 queryset = Sitting.objects.all() ,获取所有记录然后根据它进行分页是不是错了?如果有数百万条记录,会不会影响性能? Sitting.objects.all() 这是一个示例@Sandhu 根据您的要求您可以过滤记录,您可以根据您的要求编写自己的查询 您应该返回带有空列表的200 OK,因为服务中确实存在 URL 和集合,但没有一个与查询参数匹配。检查***.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 保持对对象的引用,不必为每个页面计算内容。实施见***.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 的子集并将一些数据添加到与用户模型无关的结果集中(我的问题代码中省略了该逻辑)跨度> 查看此链接:django-rest-framework.org/api-guide/viewsets/… 还是不行 :( 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 的样式。

【讨论】:

以上是关于Django Rest Framework:在 ViewSet 上打开分页(如 ModelViewSet 分页)的主要内容,如果未能解决你的问题,请参考以下文章

Django中rest_framework的认证组件,权限组件,频率组件,序列化组件的最简化版接口

如何在 django-rest-framework 中为 API 使用 TokenAuthentication

在 django-rest-framework 中插入 django-allauth 作为端点

django使用rest_framework

Django rest framework 身份和权限验证

Django Rest Framework