默认情况下 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 结果页面后发现右上角多出了过滤器功能,默认情况下会对字段进行精准搜索,但经过配置后可达到模糊搜索的标准。
详见 https://django-filter.readthedocs.io/en/master/guide/rest_framework.html#adding-a-filterset-with-filterset-class
如想扩展标准 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
由于实现过于简单,在此不做多余讲解。。。