ページネーション
Djangoは、ページ分割されたデータ(つまり、複数のページに分割され、「前へ/次へ」リンクを持つデータ)を管理するのに役立ついくつかのクラスを提供しています。
REST frameworkは、カスタマイズ可能なページネーションスタイルをサポートしています。これにより、大規模な結果セットを個々のデータページに分割する方法を変更できます。
ページネーションAPIは、次のいずれかをサポートできます。
- レスポンスのコンテンツの一部として提供されるページネーションリンク。
Content-Range
やLink
などのレスポンスヘッダーに含まれるページネーションリンク。
現在、組み込みのスタイルはすべて、レスポンスのコンテンツの一部として含まれるリンクを使用しています。このスタイルは、ブラウザブルAPIを使用する場合によりアクセスしやすくなります。
ジェネリックビューまたはviewsetsを使用している場合にのみ、ページネーションは自動的に実行されます。通常のAPIView
を使用している場合は、ページネーションされたレスポンスを返すために、自分でページネーションAPIを呼び出す必要があります。例については、mixins.ListModelMixin
とgenerics.GenericAPIView
クラスのソースコードを参照してください。
ページネーションクラスをNone
に設定することで、ページネーションをオフにすることができます。
ページネーションスタイルの設定
ページネーションスタイルは、DEFAULT_PAGINATION_CLASS
とPAGE_SIZE
設定キーを使用してグローバルに設定できます。たとえば、組み込みのlimit/offsetページネーションを使用するには、次のようにします。
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リファレンス
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": [
…
]
}
設定
PageNumberPagination
スタイルをグローバルに有効にするには、次の構成を使用し、必要に応じてPAGE_SIZE
を設定します。
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100
}
GenericAPIView
サブクラスでは、pagination_class
属性を設定して、ビューごとにPageNumberPagination
を選択することもできます。
設定
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でページネーションコントロールをレンダリングする際に使用するテンプレートの名前。レンダリングスタイルを変更するためにオーバーライドしたり、HTMLページネーションコントロールを完全に無効にするためにNone
に設定したりできます。デフォルトは"rest_framework/pagination/numbers.html"
です。
LimitOffsetPagination
このページネーションスタイルは、複数のデータベースレコードを検索する際に使用される構文を反映しています。クライアントは、「limit」と「offset」の両方のクエリパラメータを含めます。limitは返すアイテムの最大数を示し、他のスタイルのpage_size
と同等です。offsetは、ページ分割されていないアイテムの完全なセットに対するクエリのはじまり位置を示します。
リクエスト:
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
- クライアントがクエリパラメータで提供しない場合に使用するlimitを示す数値。PAGE_SIZE
設定キーと同じ値をデフォルトで使用します。limit_query_param
- 「limit」クエリパラメータの名前を示す文字列値。デフォルトは'limit'
です。offset_query_param
- 「offset」クエリパラメータの名前を示す文字列値。デフォルトは'offset'
です。max_limit
- 設定されている場合、これはクライアントが要求できる最大許容limitを示す数値です。デフォルトはNone
です。template
- ブラウザブルAPIでページネーションコントロールをレンダリングする際に使用するテンプレートの名前。レンダリングスタイルを変更するためにオーバーライドしたり、HTMLページネーションコントロールを完全に無効にするためにNone
に設定したりできます。デフォルトは"rest_framework/pagination/numbers.html"
です。
CursorPagination
カーソルベースのページネーションは、クライアントが結果セットをページ分割するために使用できる不透明な「カーソル」インジケーターを表示します。このページネーションスタイルは、前方と後方のコントロールのみを表示し、クライアントが任意の位置に移動することを許可しません。
カーソルベースのページネーションでは、結果セットに一意で不変のアイテムの順序がある必要があります。この順序付けは、通常、レコードの作成タイムスタンプである可能性があり、これにより、ページ分割するための整合性のある順序付けが提供されます。
カーソルベースのページネーションは、他のスキームよりも複雑です。また、結果セットに固定された順序付けが必要であり、クライアントが結果セットに任意にインデックス付けすることを許可しません。ただし、次の利点があります。
- 一貫したページネーションビューを提供します。適切に使用した場合、
CursorPagination
は、ページ分割処理中に他のクライアントによって新しいアイテムが挿入されても、クライアントが同じアイテムを2回見ることはないことを保証します。 - 非常に大規模なデータセットでの使用をサポートします。非常に大規模なデータセットでは、オフセットベースのページネーションスタイルを使用したページネーションが非効率的または使用できなくなる可能性があります。代わりに、カーソルベースのページネーションスキームは固定時間プロパティを持ち、データセットのサイズが増加しても遅くなりません。
詳細と制限事項
カーソルベースのページネーションの適切な使用には、細心の注意が必要です。スキームを適用する順序について検討する必要があります。デフォルトは"-created"
で注文することです。これは、**モデルインスタンスに「created」タイムスタンプフィールドが存在する必要がある**ことを前提としており、最近追加されたアイテムを最初に表示する「タイムライン」スタイルのページネーションビューを提供します。
ページネーションクラスの'ordering'
属性をオーバーライドするか、OrderingFilter
フィルタクラスとCursorPagination
を一緒に使用することで、順序付けを変更できます。OrderingFilter
と一緒に使用する場合、ユーザーが注文できるフィールドを厳しく制限することを強くお勧めします。
カーソルページネーションの適切な使用には、次の条件を満たす順序付けフィールドが必要です。
- タイムスタンプ、スラッグ、または作成時にのみ設定される他のフィールドなど、不変の値にする必要があります。
- 一意であるか、ほぼ一意である必要があります。ミリ秒精度のタイムスタンプが良い例です。このカーソルページネーションの実装では、スマートな「位置プラスオフセット」スタイルを使用しているため、順序付けとして厳密に一意ではない値を適切にサポートできます。
- 文字列に変換できるnull許容ではない値にする必要があります。
- 浮動小数点数であってはいけません。精度エラーにより、簡単に間違った結果につながります。ヒント:代わりに10進数を使用してください。(既に浮動小数点フィールドがあり、そのフィールドでページネーションする必要がある場合は、精度の制限に10進数を使用する
CursorPagination
サブクラスの例はこちらにあります。) - フィールドにはデータベースインデックスが必要です。
これらの制約を満たさない順序付けフィールドを使用しても、一般的には機能しますが、カーソルページネーションの利点の一部を失うことになります。
カーソルページネーションの実装に関するより技術的な詳細については、"Building cursors for the 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" クエリパラメータの名前を示す文字列値。デフォルトは'cursor'
です。ordering
= カーソルベースのページングが適用されるフィールドを示す文字列、または文字列のリスト。例:ordering = 'slug'
。デフォルトは-created
です。この値は、ビューでOrderingFilter
を使用することで上書きすることもできます。template
= ブラウザブルAPIでページングコントロールをレンダリングする際に使用するテンプレートの名前。レンダリングスタイルを変更するために上書きしたり、HTMLページングコントロールを完全に無効にするためにNone
に設定したりできます。デフォルトは"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
ヘッダーが含まれるようになります。例:
'Link' ヘッダーを使用したカスタムページングスタイル
HTMLページネーションコントロール
デフォルトでは、ページングクラスを使用すると、ブラウザブルAPIにHTMLページングコントロールが表示されます。2つの組み込み表示スタイルがあります。PageNumberPagination
クラスとLimitOffsetPagination
クラスは、前後のコントロール付きのページ番号のリストを表示します。CursorPagination
クラスは、前後のコントロールのみを表示する、よりシンプルなスタイルを表示します。
コントロールのカスタマイズ
HTMLページングコントロールをレンダリングするテンプレートをオーバーライドできます。2つの組み込みスタイルは次のとおりです。
rest_framework/pagination/numbers.html
rest_framework/pagination/previous_and_next.html
グローバルテンプレートディレクトリにこれらのパスのいずれかを含むテンプレートを提供すると、関連するページングクラスのデフォルトのレンダリングがオーバーライドされます。
または、既存のクラスのいずれかをサブクラス化し、クラスにtemplate = None
属性を設定することで、HTMLページングコントロールを完全に無効にすることができます。その後、DEFAULT_PAGINATION_CLASS
設定キーを設定して、カスタムクラスをデフォルトのページングスタイルとして使用する必要があります。
低レベルAPI
ページングクラスがコントロールを表示するかどうかを決定するための低レベルAPIは、ページングインスタンスのdisplay_page_controls
属性として公開されています。カスタムページングクラスは、HTMLページングコントロールの表示が必要な場合、paginate_queryset
メソッドでTrue
に設定する必要があります。
コントロールのレンダリング方法をさらにカスタマイズするために、.to_html()
メソッドと.get_html_context()
メソッドをカスタムページングクラスでオーバーライドすることもできます。
サードパーティパッケージ
次のサードパーティパッケージも利用できます。
DRF-extensions
DRF-extensions
パッケージには、APIクライアントが最大許容ページサイズを取得するために?page_size=max
を指定できるようにするPaginateByMaxMixin
ミックスインクラスが含まれています。
drf-proxy-pagination
drf-proxy-pagination
パッケージには、クエリパラメータを使用してページングクラスを選択できるProxyPagination
クラスが含まれています。
link-header-pagination
django-rest-framework-link-header-pagination
パッケージには、GitHub REST APIドキュメントで説明されているように、HTTP Link
ヘッダーを介してページングを提供するLinkHeaderPagination
クラスが含まれています。