Filtering
“ 由Django Manager提供的根QuerySet描述了數據庫表中的所有對象。可是通常你需要的只是選擇完整對象中的一個子集而已。
—— Django文檔 ”
REST framework列表視圖的默認行為是返回一個model的全部queryset。通常你卻想要你的API來限制queryset返回的數據。
最簡單的過濾任意GenericAPIView
子視圖queryset的方法就是重寫它的.get_queryset()
方法。
重寫這個方法允許你使用很多不同的方式來定制視圖返回的queryset。
Filtering against the current user(根據當前用戶進行過濾)
您可能想要過濾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)
Filtering against the URL(根據URL進行過濾)
另一種過濾方式可能包括基於URL的某些部分來限制queryset。
例如,如果你的URL配置包含一個參數如下:
url('^purchases/(?P<username>.+)/$', PurchaseList.as_view()),
你就可以寫一個view,返回基於URL中的username參數進行過濾的結果。
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)
Filtering against query parameters(根據查詢參數進行過濾)
過濾初始查詢集的最后一個示例是基於url中的查詢參數確定初始查詢集。
我們可以通過重寫.get_queryset()
方法來處理像http://example.com/api/purchases?username=denvercoder9
這樣的網址,並且只有在URL中包含username
參數時,才過濾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', None) if username is not None: queryset = queryset.filter(purchaser__username=username) return queryset
Generic Filtering(通用過濾)
除了能夠重寫默認的queryset,REST框架還包括對通用過濾后端的支持,允許你輕松構建復雜的檢索器和過濾器。
通用過濾器也可以在browsable API和admin API中顯示為HTML控件。
Setting filter backends(設置通用過濾后端)
默認過濾器后端可以在全局設置中使用DEFAULT_FILTER_BACKENDS
來配置。例如。
REST_FRAMEWORK = { 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) }
你還可以使用基於GenericAPIView
類的視圖在每個view或每個viewset基礎上設置過濾器后端。
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,)
DjangoFilterBackend(Django過濾后端)
django-filter
庫包含一個為REST framework提供高度可定制字段過濾的DjangoFilterBackend
類。
要使用DjangoFilterBackend
,首先要先安裝django-filter
。
pip install django-filter
現在,你需要將filter backend 添加到你django project的settings中:
REST_FRAMEWORK = { 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) }
或者你也可以將filter backend添加到一個單獨的view或viewSet中:
from django_filters.rest_framework import DjangoFilterBackend class UserListView(generics.ListAPIView): ... filter_backends = (DjangoFilterBackend,)
如果你正在使用 browsable API或 admin API,你還需要安裝django-crispy-forms
,通過使用Bootstarp3渲染來提高filter form在瀏覽器中的展示效果。
pip install django-crispy-forms
安裝完成后,將crispy-forms
添加到你Django project的INSTALLED_APPS
中,browsable API將為DjangoFilterBackend
提供一個像下面這樣的filter control:
Specifying filter fields(指定篩選字段)
如果你的需求都是些簡單相等類型的篩選,那么你可以在你的view或viewSet里面設置一個filter_fields
屬性,列出所有你想依靠篩選的字段集合。
class ProductList(generics.ListAPIView): queryset = Product.objects.all() serializer_class = ProductSerializer filter_backends = (filters.DjangoFilterBackend,) filter_fields = ('category', 'in_stock')
這將為你列出來的字段自動創建一個filterSet,允許你想下面這樣請求篩選結果:
http://example.com/api/products?category=clothing&in_stock=True
Specifying a FilterSet(指定FilterSet)
對於更高級的過濾要求,你可以指定在view中應該使用的FilterSet
類。例如:
import django_filters from myapp.models import Product from myapp.serializers import ProductSerializer from rest_framework import generics class ProductFilter(django_filters.rest_framework.FilterSet): min_price = django_filters.NumberFilter(name="price", lookup_expr='gte') max_price = django_filters.NumberFilter(name="price", lookup_expr='lte') class Meta: model = Product fields = ['category', 'in_stock', 'min_price', 'max_price'] class ProductList(generics.ListAPIView): queryset = Product.objects.all() serializer_class = ProductSerializer filter_backends = (django_filters.rest_framework.DjangoFilterBackend,) filter_class = ProductFilter
這將允許你向下面一樣發出請求,如:
http://example.com/api/products?category=clothing&max_price=10.00
你也可以使用django-filter
跨越關系,讓我們假設每個產品都有Manufacturer
模型的外鍵,所以我們創建過濾器使用Manufacturer
名稱過濾。例如:
import django_filters from myapp.models import Product from myapp.serializers import ProductSerializer from rest_framework import generics class ProductFilter(django_filters.rest_framework.FilterSet): class Meta: model = Product fields = ['category', 'in_stock', 'manufacturer__name']
這使我們能夠進行如下查詢:
http://example.com/api/products?manufacturer__name=foo
SearchFilter(搜索過濾)
SearchFilter
類支持基於簡單單查詢參數的搜索,並且基於Django admin的搜索功能。
在使用時, browsable API將包括一個SearchFilter
控件:
僅當view中設置了search_fields
屬性時,才應用SearchFilter
類。search_fields
屬性應該是model中文本類型字段的名稱列表,例如CharField
或TextField
。
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')
默認情況下,搜索將使用不區分大小寫的部分匹配。 搜索參數可以包含多個搜索項,其應該是空格和/或逗號分隔。 如果使用多個搜索術語,則僅當所有提供的術語都匹配時才在列表中返回對象。
可以通過在search_fields
前面添加各種字符來限制搜索行為。
-
'^' 以指定內容開始.
-
'=' 完全匹配
-
'@' 全文搜索(目前只支持Django的MySQL后端)
-
'$' 正則搜索
例如:
search_fields = ('=username', '=email')
默認情況下,搜索參數名為'search'
,但這可以通過使用SEARCH_PARAM
設置覆蓋。
OrderingFilter(排序篩選)
OrderingFilter
類支持簡單的查詢參數控制結果排序。
默認情況下,查詢參數名為'ordering'
,但這可以通過使用ORDERING_PARAM
設置覆蓋。
例如,按用戶名排序用戶:
默認情況下,查詢參數名為'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
Specifying which fields may be ordered against(指定支持排序的字段)
建議你明確指定API應在ordering filter中允許哪些字段。您可以通過在view中設置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
屬性指定的serializer上的任何可讀字段進行過濾。
如果你確信視圖正在使用的queryset不包含任何敏感數據,則還可以通過使用特殊值'__all__'
來明確指定view應允許對任何model字段或queryset進行排序。
class BookingsListView(generics.ListAPIView): queryset = Booking.objects.all() serializer_class = BookingSerializer filter_backends = (filters.OrderingFilter,) ordering_fields = '__all__'
Specifying a default ordering(指定默認排序)
如果在view中設置了ordering
屬性,則將把它用作默認排序。
通常,你可以通過在初始queryset上設置order_by
來控制此操作,但是使用view中的ordering
參數允許你以某種方式指定排序,然后可以將其作為上下文自動傳遞到呈現的模板。如果它們用於排序結果的話就能使自動渲染不同的列標題成為可能。
class UserListView(generics.ListAPIView): queryset = User.objects.all() serializer_class = UserSerializer filter_backends = (filters.OrderingFilter,) ordering_fields = ('username', 'email') ordering = ('username',)
ordering
屬性可以是字符串或字符串的列表/元組。