Rest-framework专栏讲解(二十三):Pagination

Rest-framework专栏讲解(二十三):Pagination此样式更易于访问。 有关示例请参见 mixins.ListModelMixin 和 generics.GenericAPIView 类的源代码。 可以通过将分页类设置为 None 来关闭分页。 两个属性 DEFAULT_PAGINATION_CLASS 和 PAGE_SIZE …

MedusaSorcerer的博客


点击跳转到 Rest-Framework 专栏目录

当你的 API 返回大数据的时候, 这时候你可能需要用到分页, 分页 API 可以支持以下任一功能:

  • 作为响应内容的一部分, 并提供分页链接
  • 响应头包含分页链接, 例如 Content-Range 或者 Link

当前内置样式均使用作为响应内容一部分包含的链接样式, 此样式更易于访问。

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

可以通过将分页类设置为 None 来关闭分页。

设置分页样式

分页样式可以使用 DEFAULT_PAGINATION_CLASSPAGE_SIZE 进行全局性设置, 例如使用内置的限制/偏移设置, 可以执行以下操作:

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

需要注意的是, 你需要设置同时分页类和页面数据大小, 两个属性 DEFAULT_PAGINATION_CLASSPAGE_SIZE 默认都是 None

你还可以使用 pagination_class 在单个视图类中指定分页类, 通常情况下, 需要保证整个系统的分页统一和样式统一, 尽管在一些特殊的情况下, 如单独设置页面返回数据的最大值或最小值。

修改分页样式

如果你需要修改默认的分页样式, 则需要覆盖其中的一个分页类, 并覆盖其中的属性值:

class LargeResultsSetPagination(PageNumberPagination):
    page_size = 1000
    page_size_query_param = 'page_size'
    max_page_size = 10000

class StandardResultsSetPagination(PageNumberPagination):
    page_size = 100
    page_size_query_param = 'page_size'
    max_page_size = 1000

然后你可以使用 pagination_class 进行视图默认样式覆盖:

class BillingRecordsView(generics.ListAPIView):
    queryset = Billing.objects.all()
    serializer_class = BillingRecordsSerializer
    pagination_class = LargeResultsSetPagination

或者使用 DEFAULT_PAGINATION_CLASS 参数进行全局性覆盖:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.StandardResultsSetPagination'
}

PageNumberPagination

此分页样式在请求时, 接受一个单数字页码。

请求:

GET https://api.example.org/accounts/?page=4

响应:

HTTP 200 OK
{
    "count": 1023,
    "next": "https://api.example.org/accounts/?page=5",
    "previous": "https://api.example.org/accounts/?page=3",
    "results": []
}

如果使用该样式, 请使用以下配置, 并根据你的需求设置 PAGE_SIZE 的数值:

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

GenericAPIView 子类上您还可以设置 pagination_class 属性, 以基于每个视图进行 PageNumberPagination 选择。

所述 PageNumberPagination 类包括多个可重写修改分页样式属性, 要设置这些属性, 您应该覆盖 PageNumberPagination 类, 然后如上所述启用您的自定义分页类。

  • django_paginator_class 要使用的 Django 分页类, 默认值为 django.core.paginator.Paginator, 在大多数情况下都可以使用
  • page_size 指示页面大小的数值, 如果设置, 它将覆盖 PAGE_SIZE 设置, 默认值为与 PAGE_SIZE 设置的值相同
  • page_query_param 一个字符串值, 指示用于分页控件的查询参数的名称
  • page_size_query_param 如果设置, 则这是一个字符串值, 指示查询参数的名称, 该参数允许客户端根据每个请求设置页面大小, 默认为 None, 表示客户端可能无法控制请求的页面大小
  • max_page_size 如果已设置, 则这是一个数字值, 指示请求的最大允许页面大小, 该属性仅在 page_size_query_param 已设置的情况下才有效
  • last_page_strings 字符串值的列表或元组, 指示可与 page_query_param 一起使用以请求集合中的最后一页的值, 默认为 ('last',)
  • template 在可浏览的 API 中呈现分页控件时要使用的模板的名称, 可以重写以修改呈现样式, 或设置为 None 完全禁用 HTML 分页控件, 默认为 "rest_framework/pagination/numbers.html"

LimitOffsetPagination

这种分页样式反映了查找多个数据库记录时使用的语法, 客户端同时包含 limitoffset 查询参数, 该限制指示要返回的最大项目数, 偏移量指示查询相对于完整的未标记项集的起始位置。

简而言之, 在你使用 limit=100&offset=2 的数据查询时, 你的偏移量是 100, 你的查询数据起始位置是 limit * (offset - 1), 反映了你在查询数据流中的起始位置以及终止位置, 类似于列表切片。

请求:

GET https://api.example.org/accounts/?limit=100&offset=400

响应:

HTTP 200 OK
{
    "count": 1023
    "next": "https://api.example.org/accounts/?limit=100&offset=500",
    "previous": "https://api.example.org/accounts/?limit=100&offset=300",
    "results": []
}

若是你需要全局性启用 LimitOffsetPagination 样式, 请使用以下配置:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'
}

您也可以选择性设置 PAGE_SIZE 数值, 如果还使用了该 PAGE_SIZE 参数, 则 limit 查询参数将是可选的, 并且客户端可以将其省略。

GenericAPIView 子类上, 您还可以设置 pagination_class 属性, 以基于每个视图进行 LimitOffsetPagination 选择。

所述 LimitOffsetPagination 类包括多个可重写修改分页样式属性, 要设置这些属性, 您应该覆盖 LimitOffsetPagination 类, 然后如上所述启用您的自定义分页类。

  • default_limit 一个数字值, 指示客户端未在查询参数中提供限制时使用的限制, 默认值为 PAGE_SIZE 的值
  • limit_query_param 字符串值, 指示 limit 查询参数的名称, 默认为 'limit'
  • offset_query_param 一个字符串值, 指示 offset 查询参数的名称, 默认为 'offset'
  • max_limit 如果设置了此值, 则该值表示客户端可能请求的最大允许限制, 默认为 None
  • template 在可浏览的 API 中呈现分页控件时要使用的模板的名称, 可以重写以修改呈现样式, 或设置为 None 完全禁用 HTML 分页控件, 默认为 "rest_framework/pagination/numbers.html"

CursorPagination

基于游标的分页提供了一个不透明的“游标”指示器, 客户端可以使用它对结果集进行分页, 这种分页样式只显示前进和后退的控件, 并且不允许客户端导航到任意位置。

基于光标的分页要求结果集中的项具有唯一的、不变的顺序, 此顺序通常可能是记录上的创建时间戳, 因为它提供了一个一致的顺序来分页。

基于光标的分页比其他方案更复杂, 它还要求结果集呈现固定的顺序, 并且不允许客户端任意索引到结果集, 但是它确实提供了以下好处:

  • 提供一致的分页视图, 如果使用得当 CursorPagination 可以确保客户端在翻阅记录时永远不会看到相同的项目两次, 即使在分页过程中其他客户端插入了新项目时也是如此
  • 支持使用非常大的数据集, 对于非常大的数据集, 如果使用基于偏移的分页样式的分页, 可能会变得效率低下或无法使用, 相反基于游标的分页方案具有固定时间属性, 并且不会随着数据集大小的增加而变慢

正确使用基于游标的分页需要稍微注意细节, 您需要考虑要针对该方案应用哪种排序, 默认设置为 "-created", 但是在模型实例上必须有一个 "created" 时间戳字段, 并且将呈现一个 "timeline" 样式的分页视图, 其中最新添加的项在前面。

正确使用游标分页应具有满足以下条件的排序字段:

  • 创建时应为不变的值, 例如时间戳、段标或仅设置一次的其他字段
  • 应该是唯一的, 或者几乎是唯一的, 毫秒精度时间戳就是一个很好的例子, 游标分页的这种实现使用了一种智能的 “位置加偏移” 样式, 允许它正确地支持不严格唯一的值作为排序值
  • 应该是可以强制转换为字符串的非空值
  • 不应该是浮点数, 精度误差很容易导致错误的结果, 提示:请改用小数 (如果您已经有一个 float 字段, 并且必须对此进行分页, 则此处提供了一个使用小数来限制精度的示例 CursorPagination 子类)
  • 该字段应具有数据库索引

使用不满足这些约束的排序字段通常仍然可以使用, 但是您将失去光标分页的一些好处。

有关用于游标分页的实现的更多技术细节, 博客文章 “为Disqus API建立游标” 对基本方法进行了很好的概述。

要全局启用 CursorPagination 样式, 请使用以下配置, PAGE_SIZE 根据需要进行修改:

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

GenericAPIView 子类上, 您还可以设置 pagination_class 属性以基于每个视图进行 CursorPagination 选择。

所述 CursorPagination 类包括多个可重写修改分页样式属性, 要设置这些属性, 您应该覆盖 CursorPagination 该类, 然后如上所述启用您的自定义分页类。

  • page_size 指示页面大小的数值, 如果设置, 则此设置将覆盖 PAGE_SIZE 设置, 默认值为与 PAGE_SIZE 设置键相同的值
  • cursor_query_param 一个字符串值, 指示 “游标” 查询参数的名称, 默认为 'cursor'
  • ordering 这应该是一个字符串或字符串列表, 指示将基于光标的分页应用于的字段, 例如:ordering='slug', 默认为 -created, 通过 OrderingFilter 在视图上使用, 也可以覆盖此值
  • template 在可浏览的 API 中呈现分页控件时要使用的模板的名称, 可以重写以修改呈现样式, 或设置为 None 完全禁用 HTML 分页控件, 默认为 "rest_framework/pagination/previous_and_next.html"

自定义分页

要创建自定义的分页序列化程序类, 您应该继承 pagination.BasePagination, 并覆盖 paginate_queryset(self, queryset, request, view=None)get_paginated_response(self, data) 方法:

  • paginate_queryset 方法将传递给初始查询集, 并且应返回仅包含所请求页面中数据的可迭代对象
  • get_paginated_response 方法将传递序列化的页面数据, 并且应返回一个 Response 实例

注意该 paginate_queryset 方法可以在分页实例上设置状态, 以后可以由该 get_paginated_response 方法使用。

假设我们想用修改后的格式替换默认的分页输出样式, 该格式在嵌套的 "links" 键中包含下一个和上一个链接, 我们可以这样指定一个自定义分页类:

class CustomPagination(pagination.PageNumberPagination):
    def get_paginated_response(self, data):
        return Response({
            'links': {
                'next': self.get_next_link(),
                'previous': self.get_previous_link()
            },
            'count': self.page.paginator.count,
            'results': data
        })

在全局配置中设置自定义分页类:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'my_project.apps.core.pagination.CustomPagination',
    'PAGE_SIZE': 100
}

请注意, 如果您关心在可浏览 API 中如何在响应中显示键的顺序, 那么在构造分页响应主体时您可能会选择使用 OrderedDict, 并且这是可选的。

使用自定义分页类

要在默认情况下使用自定义分页类, 请使用以下 DEFAULT_PAGINATION_CLASS 设置:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'my_project.apps.core.pagination.LinkHeaderPagination',
    'PAGE_SIZE': 100
}

列表端点的 API 响应现在将包含 Link 标头, 而不是将分页链接作为响应主体的一部分, 例如:

Rest-framework专栏讲解(二十三):Pagination

分页和模式

您还可以通过实现 get_schema_fields() 方法, 使分页控件可用于 REST 框架提供的模式自动生成, 此方法应具有以下签名:

get_schema_fields(self, view)

该方法应返回 coreapi.Field 实例列表。

HTML 分页控件

默认情况下使用分页类将导致 HTML 分页控件显示在可浏览的 API 中, 有两种内置显示样式, PageNumberPaginationLimitOffsetPagination 类显示带有上一个和下一个控件的页码列表, CursorPagination 类显示的样式更简单, 只显示上一个和下一个控件。

自定义控件

您可以覆盖呈现 HTML 分页控件的模板, 两种内置样式是:

  • rest_framework/pagination/numbers.html
  • rest_framework/pagination/previous_and_next.html

在全局模板目录中提供具有这些路径之一的模板, 将覆盖相关分页类的默认呈现。

另外您可以通过将现有类的子类设置 template=None 为该类的属性, 来完全禁用 HTML 分页控件, 然后您需要配置 DEFAULT_PAGINATION_CLASS 设置, 以将自定义类用作默认的分页样式。

分页实例上的 display_page_controls用于确定分页类是否应显示控件的低级 API, 如果自定义分页类需要显示 HTML 分页控件, 则应在 paginate_queryset 方法中将其设置为 True

为了进一步自定义控件的呈现方式, 还可以在自定义分页类中重写 .to_html().get_html_context() 方法。

扩展

今天的文章Rest-framework专栏讲解(二十三):Pagination分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/22238.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注