pagination.py

ページネーション

Djangoは、ページ分割されたデータ(つまり、複数のページに分割され、「前へ/次へ」リンクを持つデータ)を管理するのに役立ついくつかのクラスを提供しています。

Djangoドキュメント

REST frameworkは、カスタマイズ可能なページネーションスタイルをサポートしています。これにより、大規模な結果セットを個々のデータページに分割する方法を変更できます。

ページネーションAPIは、次のいずれかをサポートできます。

  • レスポンスのコンテンツの一部として提供されるページネーションリンク。
  • Content-RangeLinkなどのレスポンスヘッダーに含まれるページネーションリンク。

現在、組み込みのスタイルはすべて、レスポンスのコンテンツの一部として含まれるリンクを使用しています。このスタイルは、ブラウザブルAPIを使用する場合によりアクセスしやすくなります。

ジェネリックビューまたはviewsetsを使用している場合にのみ、ページネーションは自動的に実行されます。通常のAPIViewを使用している場合は、ページネーションされたレスポンスを返すために、自分でページネーションAPIを呼び出す必要があります。例については、mixins.ListModelMixingenerics.GenericAPIViewクラスのソースコードを参照してください。

ページネーションクラスをNoneに設定することで、ページネーションをオフにすることができます。

ページネーションスタイルの設定

ページネーションスタイルは、DEFAULT_PAGINATION_CLASSPAGE_SIZE設定キーを使用してグローバルに設定できます。たとえば、組み込みの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設定キーを使用して、グローバルにスタイルを適用します。例:

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 Header

'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クラスが含まれています。

django-rest-framework-link-header-paginationパッケージには、GitHub REST APIドキュメントで説明されているように、HTTP Linkヘッダーを介してページングを提供するLinkHeaderPaginationクラスが含まれています。