Django REST framework API 指南(17):分頁

wcode發表於2019-03-04

官方原文連結
本系列文章 github 地址
轉載請註明出處

分頁

REST framework 包含對可定製分頁樣式的支援。這使你可以將較大的結果集分成單獨的資料頁面。

分頁 API 支援:

  • 以分頁連結的形式作為響應內容的一部分。
  • 以分頁連結的形式包含在響應的 header 中,如 Content-RangeLink.

內建的樣式目前是以分頁連結的形式作為響應內容的一部分。使用可瀏覽的 API 時,此樣式更易於訪問。

分頁僅在你使用通用檢視或檢視集時自動執行。如果你使用的是常規 APIView,則需要自己呼叫分頁 API 以確保返回分頁響應。示例請參閱 mixins.ListModelMixingenerics.GenericAPIView 類的原始碼。

可以通過將分頁類設定為 None,關閉分頁。

設定分頁樣式

分頁樣式可以使用 DEFAULT_PAGINATION_CLASSPAGE_SIZE setting key 全域性設定。例如,要使用內建的 limit/offset 分頁,你可以這樣做:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 100
}
複製程式碼

請注意,你需要設定分頁類和應使用的頁面大小。預設情況下,DEFAULT_PAGINATION_CLASSPAGE_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 setting key 全域性應用樣式。例如:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.StandardResultsSetPagination'
}
複製程式碼

API 參考

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 setting。預設值與 PAGE_SIZE setting key 相同。
  • 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” 查詢引數。limit 表示要返回的 item 的最大數量,並且等同於其他樣式中的 page_size。offset 指定查詢的起始位置與完整的未分類 item 集的關係。

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 - 一個數字值,指定客戶端在查詢引數中未提供的 limit 。預設值與 PAGE_SIZE setting key 相同。
  • limit_query_param - 一個字串值,指示 “limit” 查詢引數的名稱。預設為 'limit'
  • offset_query_param - 一個字串值,指示 “offset” 查詢引數的名稱。預設為 'offset'
  • max_limit - 一個數字值,表示客戶端可以要求的最大允許 limit。預設為 None
  • template - 在可瀏覽 API 中渲染分頁控制元件時使用的模板的名稱。可能會被覆蓋以修改渲染樣式,或設定為 None 以完全禁用 HTML 分頁控制元件。預設為 "rest_framework/pagination/numbers.html"

CursorPagination

基於遊標的分頁提供了一個不透明的 “遊標” 指示器,客戶端可以使用該指示器來翻閱結果集。此分頁樣式僅提供前向和反向控制元件,並且不允許客戶端導航到任意位置。

基於遊標的分頁需要在結果集中存在唯一的,不變的 item 順序。這種排序通常可以是記錄上的建立時間戳,因為這確保了排序的一致性。

基於遊標的分頁比其他方案更復雜。它還要求結果集渲染固定順序,並且不允許客戶端任意索引結果集。但它確實提供了以下好處:

  • 提供一致的分頁檢視。正確使用時 CursorPagination 確保客戶端在分頁時不會看到同一個 item,即使在分頁過程中其他客戶端正在插入新 item。
  • 支援使用非常大的資料集。使用極大資料集分頁時,使用基於偏移量的分頁樣式可能會變得效率低下或無法使用。基於遊標的分頁方案具有固定時間屬性,並且不會隨著資料集大小的增加而減慢。

細節和限制

正確使用基於遊標的分頁需要稍微注意細節。你需要考慮希望將該方案應用於何種順序。預設是按 "-created" 排序。這假設在模型例項上必須有一個 “created” 時間戳欄位,並且會渲染一個 “時間軸” 樣式分頁檢視,其中最近新增的 item 是第一個。

你可以通過重寫分頁類上的 'ordering' 屬性或者將 OrderingFilter 過濾器類與 CursorPagination 一起使用來修改排序。與 OrderingFilter 一起使用時,你應該考慮限制使用者可以排序的欄位。

正確使用遊標分頁應該有一個滿足以下條件的排序欄位:

  • 在建立時應該是一個不變的值,例如時間戳,slug,或其他只設定一次的欄位。
  • 應該是獨特的,或幾乎獨一無二的。毫秒精度時間戳就是一個很好的例子。這種遊標分頁的實現使用了一種智慧的 “位置 + 偏移” 風格,允許它正確地支援非嚴格唯一的值作為排序。
  • 應該是可以強制為字串的非空值。
  • 不應該是一個 float。精度錯誤很容易導致錯誤的結果。提示:改用小數。(如果你已經有一個 float 欄位並且必須對其進行分頁,則可以在此處找到使用小數來限定精度的示例。)
  • 該欄位應該有一個資料庫索引。

使用不滿足這些約束條件的排序欄位通常仍然有效,但是你將失去遊標分頁的一些好處。

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 setting key 相同。
  • 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” key(包含上一頁,下一頁連結)。我們可以像這樣指定一個自定義分頁類:

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 setting:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'my_project.apps.core.pagination.LinkHeaderPagination',
    'PAGE_SIZE': 100
}
複製程式碼

列表端點的 API 響應現在將包含一個 Link header,而不是將分頁連結包含為響應主體的一部分。

分頁和模式

通過實現 get_schema_fields() 方法,你還可以使分頁控制元件可用於 REST framework 提供的模式自動生成。此方法應具有以下簽名:

get_schema_fields(self, view)

該方法應該返回一個 coreapi.Field 例項列表。


Link Header

自定義分頁樣式,使用 'Link' header


HTML 分頁控制元件

預設情況下,使用分頁類將導致 HTML 分頁控制元件顯示在可瀏覽的 API 中。有兩種內建顯示樣式。 PageNumberPaginationLimitOffsetPagination 類顯示包含上一頁和下一頁控制元件的頁碼列表。 CursorPagination 類顯示更簡單的樣式,只顯示上一頁和下一頁控制元件。

自定義控制元件

你可以覆蓋渲染 HTML 分頁控制元件的模板。這兩種內建式樣是:

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

在全域性模板目錄中提供具有這些路徑的模板將覆蓋相關分頁類的預設渲染。

或者,你可以通過在現有類的子類上完全禁用 HTML 分頁控制元件,將 template=None 設定為該類的屬性。然後,你需要配置你的 DEFAULT_PAGINATION_CLASS setting key,以將你的自定義類用作預設分頁樣式。

低階 API

用於確定分頁類是否應顯示控制元件的低階 API 作為分頁例項上的 display_page_controls 屬性公開。如果需要顯示HTML 分頁控制元件,自定義分頁類應該在 paginate_queryset 方法中設定為 True

.to_html().get_html_context() 方法也可以在自定義分頁類中重寫,以便進一步自定義控制元件的渲染方式。

相關文章