Pagination

Django提供了一些类来帮助您管理分页数据 - 即分布在多个页面上的数据,带有“上一页/下一页”链接。

REST框架包括对可自定义分页样式的支持。这允许您修改将大型结果集拆分为单个数据页的方式。
分页API可以支持:

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

内置样式目前都使用包含在响应内容中的链接。使用可浏览API时,此样式更易于访问。

只有在使用通用视图或视图集时( generic views or viewsets),才会自动执行分页。如果您使用常规APIView,则需要自己调用分页API以确保返回分页响应。有关示例,请参阅mixins.ListModelMixin和generics.GenericAPIView类的源代码。

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

设置分页样式

可以使用DEFAULT_PAGINATION_CLASS和PAGE_SIZE设置键全局设置分页样式。例如,要使用内置限制/偏移分页,您可以执行以下操作:

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

请注意,您需要同时设置分页类和应使用的页面大小。 DEFAULT_PAGINATION_CLASS和PAGE_SIZE都默认为None。
您还可以使用pagination_class属性在单个视图上设置分页类。通常,您希望在整个API中使用相同的分页样式,尽管您可能希望基于每个视图改变分页的各个方面,例如默认或最大页面大小。

修改分页样式

如果要修改分页样式的特定方面,则需要覆盖其中一个分页类,并设置要更改的属性。

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'
}

API Reference

PageNumberPagination

此分页样式在请求查询参数中接受单个数字页码。
Request:

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

Response:

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

Setup

要全局启用PageNumberPagination样式,请使用以下配置,并根据需要设置PAGE_SIZE:

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

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

Configuration

PageNumberPagination类包含许多可以重写以修改分页样式的属性。

要设置这些属性,您应该覆盖PageNumberPagination类,然后启用上面的自定义分页类。

  • django_paginator_class - 要使用的Django Paginator类。默认是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

这种分页样式反映了查找多个数据库记录时使用的语法。 客户端包括“limit”和“offset”查询参数。 限制表示要返回的最大项目数,相当于其他样式中的page_size。 偏移量(offset)表示查询相对于整套未标记项目的起始位置。
Request:

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

Response:

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": [
       …
    ]
}

Setup

要全局启用LimitOffsetPagination样式,请使用以下配置:

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

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

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

Configuration

LimitOffsetPagination类包含许多可以重写以修改分页样式的属性。

要设置这些属性,您应该覆盖LimitOffsetPagination类,然后启用上面的自定义分页类。

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

CursorPagination

基于光标的分页呈现不透明的“光标”指示符,客户端可以使用该指示符来翻阅结果集。 这种分页样式仅呈现正向和反向控件,并且不允许客户端导航到任意位置。

基于游标的分页要求结果集中的项具有唯一且不变的排序。 这种排序通常可以是记录上的创建时间戳,因为这提供了一致的分页排序。

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

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

Details and limitations

正确使用基于光标的分页需要注意细节。 您需要考虑您希望应用该方案的顺序。 默认是按“-created”排序。 这假定模型实例上必须存在“已创建”的时间戳字段,并且将呈现“时间轴”样式的分页视图,其中最近添加的项目是第一个。

您可以通过覆盖分页类的“ordering”属性,或者将OrderingFilter过滤器类与CursorPagination一起使用来修改排序。 与OrderingFilter一起使用时,您应该强烈考虑限制用户可能订购的字段。

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

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

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

有关我们用于游标分页的实现的更多技术细节,“为Disqus API构建游标”博客文章提供了基本方法的良好概述。

Setup

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

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

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

Configuration

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”。

Custom pagination styles

要创建自定义分页序列化程序类,您应该继承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方法使用该状态。

Example

假设我们想要使用包含嵌套“链接”键下的下一个和上一个链接的修改格式替换默认分页输出样式。我们可以像这样指定一个自定义分页类:

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,但这是可选的。

Using your custom pagination class

要默认使用自定义分页类,请使用DEFAULT_PAGINATION_CLASS设置:

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

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

Pagination & schemas

您还可以通过实现get_schema_fields()方法使分页控件可用于REST框架提供的模式自动生成。此方法应具有以下签名:
get_schema_fields(self, view)
该方法应返回coreapi.Field实例的列表。

A custom pagination style, using the 'Link' header'

HTML pagination controls

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

Customizing the controls

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

  • rest_framework/pagination/numbers.html
  • rest_framework/pagination/previous_and_next.html
    在全局模板目录中提供具有这些路径之一的模板将覆盖相关分页类的默认呈现。
    或者,您可以通过继承现有类来完全禁用HTML分页控件,将template = None设置为类的属性。 然后,您需要配置DEFAULT_PAGINATION_CLASS设置键以将自定义类用作默认分页样式。

Low-level API

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

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

Third party packages

还提供以下第三方软件包。

DRF-extensions

DRF-extensions软件包包含一个PaginateByMaxMixin mixin类,允许您的API客户端指定?page_size = max以获取允许的最大页面大小。

drf-proxy-pagination

drf-proxy-pagination包中包含一个ProxyPagination类,允许选择带有查询参数的分页类。

link-header-pagination

django-rest-framework-link-header-pagination包中包含一个LinkHeaderPagination类,它通过Github开发者文档中描述的HTTP Link头提供分页。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容