filters.py

フィルタリング

マネージャーによって提供されるルートクエリセットは、データベーステーブル内のすべてのオブジェクトを表します。しかし、通常は、オブジェクトの完全なセットのサブセットのみを選択する必要があります。

Djangoドキュメント

REST frameworkのジェネリックリストビューのデフォルトの動作は、モデルマネージャーのクエリセット全体を返すことです。多くの場合、APIでクエリセットによって返されるアイテムを制限する必要があります。

GenericAPIViewをサブクラス化する任意のビューのクエリセットをフィルタリングする最も簡単な方法は、.get_queryset()メソッドをオーバーライドすることです。

このメソッドをオーバーライドすると、さまざまな方法でビューによって返されるクエリセットをカスタマイズできます。

現在のユーザーに対するフィルタリング

リクエストを行っている現在認証されているユーザーに関連する結果のみが返されるように、クエリセットをフィルタリングする必要がある場合があります。

request.userの値に基づいてフィルタリングすることで、これを行うことができます。

例:

from myapp.models import Purchase
from myapp.serializers import PurchaseSerializer
from rest_framework import generics

class PurchaseList(generics.ListAPIView):
    serializer_class = PurchaseSerializer

    def get_queryset(self):
        """
        This view should return a list of all the purchases
        for the currently authenticated user.
        """
        user = self.request.user
        return Purchase.objects.filter(purchaser=user)

URLに対するフィルタリング

別のフィルタリングスタイルには、URLの一部に基づいてクエリセットを制限することが含まれます。

たとえば、URL設定に次のようなエントリが含まれている場合:

re_path('^purchases/(?P<username>.+)/$', PurchaseList.as_view()),

URLのユーザー名部分によってフィルタリングされた購入クエリセットを返すビューを作成できます。

class PurchaseList(generics.ListAPIView):
    serializer_class = PurchaseSerializer

    def get_queryset(self):
        """
        This view should return a list of all the purchases for
        the user as determined by the username portion of the URL.
        """
        username = self.kwargs['username']
        return Purchase.objects.filter(purchaser__username=username)

クエリパラメータに対するフィルタリング

初期クエリセットをフィルタリングする最後の例は、URLのクエリパラメータに基づいて初期クエリセットを決定することです。

http://example.com/api/purchases?username=denvercoder9などのURLを処理し、usernameパラメータがURLに含まれている場合にのみクエリセットをフィルタリングするために、.get_queryset()をオーバーライドできます。

class PurchaseList(generics.ListAPIView):
    serializer_class = PurchaseSerializer

    def get_queryset(self):
        """
        Optionally restricts the returned purchases to a given user,
        by filtering against a `username` query parameter in the URL.
        """
        queryset = Purchase.objects.all()
        username = self.request.query_params.get('username')
        if username is not None:
            queryset = queryset.filter(purchaser__username=username)
        return queryset

ジェネリックフィルタリング

デフォルトのクエリセットをオーバーライドできるだけでなく、REST frameworkは、複雑な検索とフィルタを簡単に構築できるジェネリックフィルタリングバックエンドもサポートしています。

ジェネリックフィルタは、ブラウズ可能なAPIと管理APIでHTMLコントロールとして表示することもできます。

Filter Example

フィルタバックエンドの設定

デフォルトのフィルタバックエンドは、DEFAULT_FILTER_BACKENDS設定を使用してグローバルに設定できます。例:

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}

GenericAPIViewクラスベースビューを使用して、ビューごとまたはビューセットごとにフィルタバックエンドを設定することもできます。

import django_filters.rest_framework
from django.contrib.auth.models import User
from myapp.serializers import UserSerializer
from rest_framework import generics

class UserListView(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    filter_backends = [django_filters.rest_framework.DjangoFilterBackend]

フィルタリングとオブジェクト参照

ビューに対してフィルタバックエンドが構成されている場合、リストビューのフィルタリングに使用されるだけでなく、単一オブジェクトを返すために使用されるクエリセットのフィルタリングにも使用されることに注意してください。

たとえば、前の例と、IDが4675の製品の場合、次のURLは、フィルタリング条件が指定された製品インスタンスによって満たされているかどうかに応じて、対応するオブジェクトを返すか、404レスポンスを返します。

http://example.com/api/products/4675/?category=clothing&max_price=10.00

初期クエリセットの上書き

オーバーライドされた.get_queryset()とジェネリックフィルタリングを同時に使用でき、すべてが期待通りに動作することに注意してください。たとえば、ProductUserとの多対多リレーションシップ(purchaseという名前)を持っている場合、次のようなビューを作成できます。

class PurchasedProductsList(generics.ListAPIView):
    """
    Return a list of all the products that the authenticated
    user has ever purchased, with optional filtering.
    """
    model = Product
    serializer_class = ProductSerializer
    filterset_class = ProductFilter

    def get_queryset(self):
        user = self.request.user
        return user.purchase_set.all()

APIガイド

DjangoFilterBackend

django-filterライブラリには、REST frameworkの高度にカスタマイズ可能なフィールドフィルタリングをサポートするDjangoFilterBackendクラスが含まれています。

DjangoFilterBackendを使用するには、まずdjango-filterをインストールします。

pip install django-filter

次に、DjangoのINSTALLED_APPS'django_filters'を追加します。

INSTALLED_APPS = [
    ...
    'django_filters',
    ...
]

これで、設定にフィルタバックエンドを追加するか

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}

個々のビューまたはビューセットにフィルタバックエンドを追加する必要があります。

from django_filters.rest_framework import DjangoFilterBackend

class UserListView(generics.ListAPIView):
    ...
    filter_backends = [DjangoFilterBackend]

単純な等価性ベースのフィルタリングのみが必要な場合は、フィルタリング対象のフィールドのセットをリストするビューまたはビューセットにfilterset_fields属性を設定できます。

class ProductList(generics.ListAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['category', 'in_stock']

これにより、指定されたフィールドに対してFilterSetクラスが自動的に作成され、次のようなリクエストを行うことができます。

http://example.com/api/products?category=clothing&in_stock=True

より高度なフィルタリング要件の場合は、ビューで使用されるFilterSetクラスを指定できます。FilterSetの詳細については、django-filterドキュメントを参照してください。DRF統合に関するセクションを読むこともお勧めします。

SearchFilter

SearchFilterクラスは、単純な単一クエリパラメータベースの検索をサポートしており、Django管理の検索機能に基づいています。

使用されている場合、ブラウズ可能なAPIにはSearchFilterコントロールが含まれます。

Search Filter

SearchFilterクラスは、ビューにsearch_fields属性が設定されている場合にのみ適用されます。search_fields属性は、CharFieldTextFieldなどのモデルのテキストタイプフィールドの名前のリストである必要があります。

from rest_framework import filters

class UserListView(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    filter_backends = [filters.SearchFilter]
    search_fields = ['username', 'email']

これにより、クライアントは次のようなクエリを行うことでリスト内のアイテムをフィルタリングできます。

http://example.com/api/users?search=russell

参照APIのアンダースコア二重表記法を使用して、ForeignKeyまたはManyToManyFieldに関する関連ルックアップを実行することもできます。

search_fields = ['username', 'email', 'profile__profession']

JSONFieldおよびHStoreFieldフィールドの場合、同じアンダースコア二重表記法を使用して、データ構造内のネストされた値に基づいてフィルタリングできます。

search_fields = ['data__breed', 'data__owner__other_pets__0__name']

デフォルトでは、検索は大文字と小文字を区別しない部分一致を使用します。検索パラメータには複数の検索用語を含めることができ、それらは空白とコンマで区切られる必要があります。複数の検索用語が使用されている場合、オブジェクトは、提供されたすべての用語が一致した場合にのみリストに返されます。検索には、スペースを含む引用符で囲まれたフレーズを含めることができ、各フレーズは単一の検索用語として扱われます。

検索動作は、フィールド名に次のいずれかの文字をプレフィックスとして付けることで指定できます(これは、フィールドに__<lookup>を追加することと同等です)。

プレフィックス ルックアップ
^ istartswith 先頭一致検索。
= iexact 完全一致。
$ iregex 正規表現検索。
@ search 全文検索(現在、DjangoのPostgreSQLバックエンドのみサポート)。
なし icontains 部分一致検索(デフォルト)。

例:

search_fields = ['=username', '=email']

デフォルトでは、検索パラメータの名前は'search'ですが、SEARCH_PARAM設定でオーバーライドできます。

リクエストコンテンツに基づいて検索フィールドを動的に変更するには、SearchFilterをサブクラス化し、get_search_fields()関数をオーバーライドできます。たとえば、次のサブクラスは、クエリパラメータtitle_onlyがリクエストにある場合にのみtitleを検索します。

from rest_framework import filters

class CustomSearchFilter(filters.SearchFilter):
    def get_search_fields(self, view, request):
        if request.query_params.get('title_only'):
            return ['title']
        return super().get_search_fields(view, request)

詳細については、Djangoドキュメントを参照してください。


OrderingFilter

OrderingFilterクラスは、単純なクエリパラメータ制御による結果の順序付けをサポートしています。

Ordering Filter

デフォルトでは、クエリパラメータの名前は'ordering'ですが、ORDERING_PARAM設定でオーバーライドできます。

たとえば、ユーザー名を基準にユーザーを並べ替えるには:

http://example.com/api/users?ordering=username

クライアントは、フィールド名の前に'-'を付けることで逆順を指定することもできます。

http://example.com/api/users?ordering=-username

複数の順序付けを指定することもできます。

http://example.com/api/users?ordering=account,username

どのフィールドを順序付けの基準にするかを指定する

APIで順序付けフィルタで許可するフィールドを明示的に指定することをお勧めします。これを行うには、次のようにビューにordering_fields属性を設定します。

class UserListView(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    filter_backends = [filters.OrderingFilter]
    ordering_fields = ['username', 'email']

これは、パスワードハッシュフィールドやその他の機密データに対する順序付けを許可するなど、予期しないデータ漏洩を防ぐのに役立ちます。

ビューにordering_fields属性を指定しない場合、フィルタクラスは、serializer_class属性で指定されたシリアライザーの読み取り可能なフィールドに対してユーザーがフィルタリングできるようにします。

ビューで使用されているクエリセットに機密データが含まれていないことを確信している場合は、特別な値'__all__'を使用して、ビューですべてのモデルフィールドまたはクエリセット集計に対する順序付けを明示的に許可することもできます。

class BookingsListView(generics.ListAPIView):
    queryset = Booking.objects.all()
    serializer_class = BookingSerializer
    filter_backends = [filters.OrderingFilter]
    ordering_fields = '__all__'

デフォルトの順序付けを指定する

ビューにordering属性が設定されている場合、これがデフォルトの順序付けとして使用されます。

通常は、初期クエリセットにorder_byを設定することでこれを制御しますが、ビューでorderingパラメータを使用すると、レンダリングされたテンプレートにコンテキストとして自動的に渡すことができる方法で順序付けを指定できます。これにより、結果の順序付けに使用されている場合に、列ヘッダーを自動的に異なる方法でレンダリングすることが可能になります。

class UserListView(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    filter_backends = [filters.OrderingFilter]
    ordering_fields = ['username', 'email']
    ordering = ['username']

ordering属性は、文字列、または文字列のリスト/タプルにすることができます。


カスタムジェネリックフィルタリング

独自のジェネリックフィルタリングバックエンドを提供したり、他の開発者が使用できるインストール可能なアプリを作成することもできます。

そのためには、BaseFilterBackendをオーバーライドし、.filter_queryset(self, request, queryset, view)メソッドをオーバーライドします。このメソッドは、新しいフィルタリングされたクエリセットを返す必要があります。

クライアントが検索とフィルタリングを実行できるようにするだけでなく、ジェネリックフィルタバックエンドは、どのオブジェクトを特定のリクエストまたはユーザーに表示する必要があるかを制限する場合にも役立ちます。

たとえば、ユーザーが作成したオブジェクトのみが表示できるように制限する必要がある場合があります。

class IsOwnerFilterBackend(filters.BaseFilterBackend):
    """
    Filter that only allows users to see their own objects.
    """
    def filter_queryset(self, request, queryset, view):
        return queryset.filter(owner=request.user)

ビューでget_queryset()をオーバーライドすることによっても同じ動作を実現できますが、フィルタバックエンドを使用すると、この制限を複数のビューに簡単に追加したり、API全体に適用したりすることができます。

インターフェースのカスタマイズ

ジェネリックフィルタは、ブラウズ可能なAPIにもインターフェースを表示できます。そのためには、フィルタのレンダリングされたHTML表現を返すto_html()メソッドを実装する必要があります。このメソッドには、次のシグネチャが必要です。

to_html(self, request, queryset, view)

このメソッドは、レンダリングされたHTML文字列を返す必要があります。

サードパーティパッケージ

以下のサードパーティパッケージは、追加のフィルタ実装を提供します。

Django REST framework filtersパッケージ

django-rest-framework-filtersパッケージは、DjangoFilterBackendクラスと連携し、リレーションシップ全体でのフィルタリングや、特定のフィールドに対する複数のフィルタ検索タイプの作成を容易にします。

Django REST framework完全一致検索フィルタ

djangorestframework-word-filterは、テキスト内の完全一致単語検索、または完全一致検索を行うfilters.SearchFilterの代替として開発されました。

Django URLフィルタ

django-url-filterは、人間にとって分かりやすいURLを介してデータを安全にフィルタリングする方法を提供します。ネスト可能であるという点で、DRFシリアライザとフィールドと非常に似ており、フィルタセットとフィルタと呼ばれます。これにより、関連データのフィルタリングが容易になります。また、このライブラリは汎用的なため、DjangoのQuerySetだけでなく、他のデータソースのフィルタリングにも使用できます。

drf-url-filters

drf-url-filterは、DRFのModelViewSetQuerysetにクリーンでシンプルかつ設定可能な方法でフィルタを適用するためのシンプルなDjangoアプリです。また、受信したクエリパラメータとその値に対する検証もサポートしています。美しいPythonパッケージであるVoluptuousが、受信したクエリパラメータの検証に使用されています。Voluptuousの最大の利点は、クエリパラメータの要件に応じて独自の検証を定義できることです。