Django Rest Framework 3.1 打破了 pagination.PaginationSerializer

Posted

技术标签:

【中文标题】Django Rest Framework 3.1 打破了 pagination.PaginationSerializer【英文标题】:Django Rest Framework 3.1 breaks pagination.PaginationSerializer 【发布时间】:2015-05-21 14:25:55 【问题描述】:

我刚刚更新到 Django Rest Framework 3.1,似乎一切都崩溃了。

在我的serializers.py 中,我有以下代码:

class TaskSerializer(serializers.ModelSerializer):
    class Meta:
    model = task
    exclude = ('key', ...)

class PaginatedTaskSerializer(pagination.PaginationSerializer):
    class Meta:
        object_serializer_class = TaskSerializer

这工作得很好。现在随着 3.1 的发布,我找不到关于如何做同样事情的示例,因为 PaginationSerializer 不再存在。 我尝试继承 PageNumberPagination 并使用其默认的 paginate_querysetget_paginated_response 方法,但我无法再将它们的结果序列化。

换句话说,我的问题是我不能再这样做了:

class Meta:
    object_serializer_class = TaskSerializer

有什么想法吗?

提前致谢

【问题讨论】:

我刚刚遇到了同样的问题。我决定升级我所有的包,这是唯一坏掉的东西。我希望这能得到答复,因为我也没有找到任何东西。 “我刚刚更新到 Django Rest Framework 3.1,似乎一切都崩溃了。”大声笑。 【参考方案1】:

我不确定这是否是完全正确的方法,但它可以满足我的需要。它使用 Django Paginator 和自定义序列化程序。

这是我的视图类,它检索对象以进行序列化

class CourseListView(AuthView):
    def get(self, request, format=None):
        """
        Returns a JSON response with a listing of course objects
        """
        courses = Course.objects.order_by('name').all()
        serializer = PaginatedCourseSerializer(courses, request, 25)
        return Response(serializer.data)

这是使用我的课程序列化器的黑客一起序列化器。

from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage

class PaginatedCourseSerializer():
    def __init__(self, courses, request, num):
        paginator = Paginator(courses, num)
        page = request.QUERY_PARAMS.get('page')
        try:
            courses = paginator.page(page)
        except PageNotAnInteger:
            courses = paginator.page(1)
        except EmptyPage:
            courses = paginator.page(paginator.num_pages)
        count = paginator.count
    
        previous = None if not courses.has_previous() else courses.previous_page_number()
        next = None if not courses.has_next() else courses.next_page_number()
        serializer = CourseSerializer(courses, many=True)
        self.data = 'count':count,'previous':previous,
                 'next':next,'courses':serializer.data

这给了我一个类似于旧分页器给出的行为的结果。


    "previous": 1,
    "next": 3,
    "courses": [...],
    "count": 384

我希望这会有所帮助。我仍然认为必须有一个更好的方法来使用新的 API 来做到这一点,但它只是没有很好地记录下来。如果我有更多的想法,我会编辑我的帖子。

编辑

我认为我找到了一种更好、更优雅的方法,即创建我自己的自定义分页器来获得像以前使用旧的分页序列化器类一样的行为。

这是一个自定义分页器类。我重载了响应和下一页方法以获得我想要的结果(即?page=2 而不是完整的 url)。

from rest_framework.response import Response
from rest_framework.utils.urls import replace_query_param

class CustomCoursePaginator(pagination.PageNumberPagination):
    def get_paginated_response(self, data):
        return Response('count': self.page.paginator.count,
                         'next': self.get_next_link(),
                         'previous': self.get_previous_link(),
                         'courses': data)

    def get_next_link(self):
        if not self.page.has_next():
            return None
        page_number = self.page.next_page_number()
        return replace_query_param('', self.page_query_param, page_number)

    def get_previous_link(self):
        if not self.page.has_previous():
            return None
        page_number = self.page.previous_page_number()
        return replace_query_param('', self.page_query_param, page_number)

那么我的课程视图和你实现它的方式非常相似,只是这次使用了自定义分页器。

class CourseListView(AuthView):
    def get(self, request, format=None):
        """
        Returns a JSON response with a listing of course objects
        """
        courses = Course.objects.order_by('name').all()
        paginator = CustomCoursePaginator()
        result_page = paginator.paginate_queryset(courses, request)
        serializer = CourseSerializer(result_page, many=True)
        return paginator.get_paginated_response(serializer.data)

现在我得到了我正在寻找的结果。


    "count": 384,
    "next": "?page=3",
    "previous": "?page=1",
    "courses": []

我仍然不确定这对 Browsable API 是如何工作的(我不使用 drf 的这个功能)。我认为您也可以为此创建自己的自定义类。我希望这会有所帮助!

【讨论】:

是的。您的解决方案有效。它还不是最佳的,因为我们无法在可浏览的 API 上获得分页链接(并且分页超链接已经消失),但目前它是唯一有效的。我们现在应该很接近了。如果您有更好的建议,请告诉我,否则我会在几天内接受您的回答。再次感谢。 这也是正确的。我们现在几乎可以做任何事情..!我相信解决方案的关键是你如何用一个简单的构造函数serializer = CourseSerializer(courses, many=True) 替换class Meta: object_serializer_class = CourseSerializer。过了那个点,我们可以让我们的想象力接管。无论如何,我更喜欢坚持默认设置,但既然你给了我“钥匙”,我接受你的回答。谢谢。【参考方案2】:

我想我想通了(至少在大多数情况下):

我们从一开始就应该使用的是:

只需使用内置分页器并将您的 views.py 更改为:

from rest_framework.pagination import PageNumberPagination

class CourseListView(AuthView):
    def get(self, request, format=None):
        """
        Returns a JSON response with a listing of course objects
        """
        courses = Course.objects.order_by('name').all()
        paginator = PageNumberPagination()
        # From the docs:
        # The paginate_queryset method is passed the initial queryset 
        # and should return an iterable object that contains only the 
        # data in the requested page.
        result_page = paginator.paginate_queryset(courses, request)
        # Now we just have to serialize the data just like you suggested.
        serializer = CourseSerializer(result_page, many=True)
        # From the docs:
        # The get_paginated_response method is passed the serialized page 
        # data and should return a Response instance.
        return paginator.get_paginated_response(serializer.data)

对于所需的页面大小,只需在settings.py 中设置PAGE_SIZE

REST_FRAMEWORK = 
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 15

您现在应该已经准备好响应正文中存在的所有选项(计数、下一个和返回链接),就像更新之前一样。

但是还有一件事仍然困扰着我:我们还应该能够获得新的html pagination controls,由于某种原因,它现在已经丢失了......

我绝对可以在这方面使用更多建议...

【讨论】:

哇。真的那么简单吗?他们真的应该用一个例子来更新文档。至于 html 链接,我不知道,我从来没有真正使用过可浏览的 API。 酷!这个解决方案为我节省了很多时间来检查他们的源代码。我希望他们尽快制作文档。 @haudoing:制作您希望在世界上看到的文档!它是开源的,文档是回馈的好方法。 我检查过,它对光标分页的工作方式相同。 Infact DRF v3.x 分页是一个巨大的改进,开箱即用。只是用法没有记录。 找到了这个答案,因为我缺少 HTML 分页控件,如果你覆盖它,你需要在 paginate_queryset 中设置 self.display_page_controls=True。 django-rest-framework.org/api-guide/pagination/#low-level-api【参考方案3】:

我意识到自发布以来已经过去了一年多,但希望这对其他人有所帮助。对我类似问题的回答是我的解决方案。我正在使用 DRF 3.2.3。

Django Rest Framework 3.2.3 pagination not working for generics.ListCreateAPIView

了解它的实现方式为我提供了获得分页所需的解决方案 + 可见 API 中的控件。

https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/mixins.py#L39

【讨论】:

以上是关于Django Rest Framework 3.1 打破了 pagination.PaginationSerializer的主要内容,如果未能解决你的问题,请参考以下文章

Django Rest Framework 和 django Rest Framework simplejwt 两因素身份验证

怎么安装django rest framework

django rest framework中文介绍

17-Django-Django REST framework-REST framework及RESTful简介

为啥 django-rest-framework 不显示 OneToOneField 数据 - django

Django:rest framework之分页(Pagination)