默認情況下 DRF generic list view 會返回整個 queryset 查詢結果,但通常業務只是需要其中一部分,這種情況下就需要使用 "過濾器" 來限制返回結果集。
最土鱉的方式是繼承 GenericAPIView 類或使用繼承了 GenericAPIView 的類,然后重寫 .get_queryset() 方法 比如改 views.py:
1 class ProductListView(generics.ListAPIView): 2 # queryset = Product.objects.all() 3 serializer_class = ProductSerializer 4 5 def get_query_set(self): 6 queryset = Product.objects.all() 7 cost = self.request.query_params.get("cost", 0) # get cost of Product object, 0 is default value 8 if click_num_min: 9 queryset = queryset.filter(price__lt=int(cost)) # filter price less than cost 10 return queryset
這樣做如果在過濾條件復雜的情況下,代碼會顯得過於冗余,而且有可能大部分代碼一直在重復實現類似的功能。
在 DRF 中通過使用如下三種方法完成簡單,高效地過濾,搜索,排序功能:
參照: https://www.django-rest-framework.org/api-guide/filtering/#api-guide
DjangoFilterBackend:
確保已經下載了 django-filters 包,Setting 文件中添加如下:
1 REST_FRAMEWORK = { 2 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) 3 }
在 views.py 文件中:
1 from django_filters.rest_framework import DjangoFilterBackend 2 3 class ProductListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): 4 queryset = Product.objects.all() # 因為有此操作又使用了過濾,所以后邊不需要重寫 get_query_set() 方法 5 serializer_class = ProductSerializer 6 pagination_class = ProductPagination 7 filter_backends = (DjangoFilterBackend,) 8 filter_fields = ('cost', 'price')
刷新 REST 結果頁面后發現右上角多出了過濾器功能,默認情況下會對字段進行精准搜索,但經過配置后可達到模糊搜索的標准。
如想擴展標准 Filter 可通過自定義 Filter 類方式實現:
1 from rest_framework import generics 2 from django_filters import rest_framework as filters 3 from myapp import Product 4 5 6 class ProductFilter(filters.FilterSet): 7 desc = filters.CharFilter(name='desc', lookup_expr='icontains') # 等價於 sql 中的 like; i 表示忽略大小寫 8 min_price = filters.NumberFilter(field_name="price", lookup_expr='gte') 9 max_price = filters.NumberFilter(field_name="price", lookup_expr='lte') 10 11 class Meta: 12 model = Product 13 fields = ['desc', 'price', 'cost] 14 15 16 class ProductList(generics.ListAPIView): 17 queryset = Product.objects.all() 18 serializer_class = ProductSerializer 19 filter_backends = (filters.DjangoFilterBackend,) 20 filterset_class = ProductFilter
注: 這里用了 ListAPIView, 在我自己的項目里使用的是高級的 GenericViewSet,由於它繼承了 GenericAPIView 所以也可以像上面例子直接調用 filters 庫完成自定義過濾功能。
SearchFilter:
這個類提供了對單一字段的搜素功能,在使用后 DRF 數據瀏覽頁面會出現 SearchFilter 的控制器。在配置時只需要將 view 中添加 search_fields 屬性,且將需要搜索的 Model 字段放入 tuple 中傳給 search_fields。
from rest_framework import filters
1 class ProductList(generics.ListAPIView): 2 queryset = Product.objects.all() 3 serializer_class = ProductSerializer 4 filter_backends = (filters.SearchFilter,) 5 search_fields = ('desc')
默認情況下,搜索是大小寫敏感的局部匹配。搜索參數可以包含多個搜索定義,且用空格或者逗號分開。如果使用了多個查詢字段則返回結果集必須符合所有所有字段查詢條件。
查詢設置:
- '^' Starts-with search.
- '=' Exact matches.
- '@' Full-text search 模糊查詢 (Currently only supported Django's MySQL backend.)
- '$' Regex search
如:search_fields = ('@username', '=email')
OrderingFilter:
OrderingFilter 類支持對單個查詢字段結果集進行排序。
from rest_framework import filters class ProductList(generics.ListAPIView): queryset = Product.objects.all() serializer_class = ProductSerializer filter_backends = (filters.SearchFilter, filters.OrderingFilter,) search_fields = ('desc') ordering_fields = ('-cost')
詳見: https://www.django-rest-framework.org/api-guide/filtering/#orderingfilter
由於實現過於簡單,在此不做多余講解。。。
