フィルタリング
マネージャーによって提供されるルートクエリセットは、データベーステーブル内のすべてのオブジェクトを表します。しかし、通常は、オブジェクトの完全なセットのサブセットのみを選択する必要があります。
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コントロールとして表示することもできます。
フィルタバックエンドの設定
デフォルトのフィルタバックエンドは、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()
とジェネリックフィルタリングを同時に使用でき、すべてが期待通りに動作することに注意してください。たとえば、Product
がUser
との多対多リレーションシップ(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
コントロールが含まれます。
SearchFilter
クラスは、ビューにsearch_fields
属性が設定されている場合にのみ適用されます。search_fields
属性は、CharField
やTextField
などのモデルのテキストタイプフィールドの名前のリストである必要があります。
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'
ですが、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のModelViewSet
のQueryset
にクリーンでシンプルかつ設定可能な方法でフィルタを適用するためのシンプルなDjangoアプリです。また、受信したクエリパラメータとその値に対する検証もサポートしています。美しいPythonパッケージであるVoluptuous
が、受信したクエリパラメータの検証に使用されています。Voluptuousの最大の利点は、クエリパラメータの要件に応じて独自の検証を定義できることです。