Django REST Framework 提供了强大的过滤功能,允许客户端通过查询参数对 API 结果进行过滤、搜索和排序。
1、过滤器工作流程
ListModelMixin的 list() -> GenericAPIView filter_queryset()- # rest_framework/mixins.py
- class ListModelMixin:
- def list(self, request, *args, **kwargs):
- # 获取初始查询集
- queryset = self.get_queryset()
-
- # 应用所有过滤器
- queryset = self.filter_queryset(queryset)
-
- # 应用分页
- page = self.paginate_queryset(queryset)
- if page is not None:
- serializer = self.get_serializer(page, many=True)
- return self.get_paginated_response(serializer.data)
-
- # 序列化并返回
- serializer = self.get_serializer(queryset, many=True)
- return Response(serializer.data)
- # rest_framework/generics.py
- class GenericAPIView:
- filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
- def filter_queryset(self, queryset):
- # 遍历所有过滤器后端
- for backend in list(self.filter_backends):
- # 调用各个过滤器的filter_queryset方法
- queryset = backend().filter_queryset(self.request, queryset, self)
- return queryset
复制代码 2、过滤器基类BaseFilterBackend
- class BaseFilterBackend:
- """
- 所有过滤器后端的基类
- """
- def filter_queryset(self, request, queryset, view):
- """
- 过滤查询集的主要方法,必须在子类中实现
- """
- raise NotImplementedError(".filter_queryset() must be overridden.")
- def get_schema_fields(self, view):
- """
- 返回 OpenAPI schema 字段
- """
- return []
- def get_schema_operation_parameters(self, view):
- """
- 返回 OpenAPI 操作参数
- """
- return []
复制代码 3、SearchFilter
SearchFilter使用 OR 逻辑连接多个字段的搜索条件- class SearchFilter(BaseFilterBackend):
- # 默认搜索参数名
- search_param = api_settings.SEARCH_PARAM
- template = 'rest_framework/filters/search.html'
- lookup_prefixes = {
- '^': 'istartswith',
- '=': 'iexact',
- '@': 'search',
- '$': 'iregex',
- }
- search_title = _('Search')
- search_description = _('A search term.')
- def get_search_fields(self, view, request):
- """从视图获取搜索字段列表"""
- return getattr(view, 'search_fields', None)
- def get_search_terms(self, request):
- """解析查询参数中的搜索条件"""
- value = request.query_params.get(self.search_param, '')
- field = CharField(trim_whitespace=False, allow_blank=True)
- cleaned_value = field.run_validation(value)
- return search_smart_split(cleaned_value)
- def construct_search(self, field_name, queryset):
- """构建搜索查询条件"""
- lookup = self.lookup_prefixes.get(field_name[0])
- if lookup:
- field_name = field_name[1:]
- else:
- # ...
- lookup = 'icontains'
- return LOOKUP_SEP.join([field_name, lookup])
- def filter_queryset(self, request, queryset, view):
- # 获取视图中定义的search_fields
- search_fields = self.get_search_fields(view, request)
- search_terms = self.get_search_terms(request)
- if not search_fields or not search_terms:
- return queryset
-
- # 构建ORM查询条件
- orm_lookups = [
- self.construct_search(str(search_field), queryset)
- for search_field in search_fields
- ]
- base = queryset
- conditions = (
- reduce(
- operator.or_,
- (models.Q(**{orm_lookup: term}) for orm_lookup in orm_lookups)
- ) for term in search_terms
- )
- queryset = queryset.filter(reduce(operator.and_, conditions))
- # 如果设置了distinct,确保结果去重
- if self.must_call_distinct(queryset, search_fields):
- queryset = queryset.filter(pk=models.OuterRef('pk'))
- queryset = base.filter(models.Exists(queryset))
- return queryset
- def get_schema_fields(self, view):
- """
- 返回 OpenAPI schema 字段
- """
- return [
- coreapi.Field(
- name=self.search_param,
- required=False,
- location='query',
- schema=coreschema.String(
- title=force_str(self.search_title),
- description=force_str(self.search_description)
- )
- )
- ]
复制代码 4、OrderingFilter
按指定字段排序,支持多字段和反向排序- class OrderingFilter(BaseFilterBackend):
- # 默认查询参数名
- ordering_param = api_settings.ORDERING_PARAM
- # 默认排序字段
- ordering_fields = None
-
- # 默认排序方向
- ordering_title = _('Ordering')
- def get_ordering(self, request, queryset, view):
- """
- 从请求中解析排序参数
- """
- params = request.query_params.get(self.ordering_param)
- if params:
- # 分割多个排序字段
- fields = [param.strip() for param in params.split(',')]
- # 过滤无效字段
- ordering = self.remove_invalid_fields(queryset, fields, view, request)
- if ordering:
- return ordering
- # 返回默认排序
- return self.get_default_ordering(view)
- def get_default_ordering(self, view):
- """
- 获取默认排序设置
- """
- ordering = getattr(view, 'ordering', None)
- if isinstance(ordering, str):
- return (ordering,)
- return ordering
-
- def filter_queryset(self, request, queryset, view):
- """
- 应用排序到查询集
- """
- ordering = self.get_ordering(request, queryset, view)
- if ordering:
- return queryset.order_by(*ordering)
- return queryset
复制代码 5、DjangoFilterBackend
DjangoFilterBackend基于 django-filter库,提供了更强大的字段过滤能力- class DjangoFilterBackend:
- # 过滤器基类
- filterset_base = filterset.FilterSet
- # 验证失败时是否引发异常
- raise_exception = True
- def get_filterset(self, request, queryset, view):
- """
- 获取过滤器集
- """
- filterset_class = self.get_filterset_class(view, queryset)
- if filterset_class is None:
- return None
- kwargs = self.get_filterset_kwargs(request, queryset, view)
- return filterset_class(**kwargs)
- def get_filterset_class(self, view, queryset=None):
- """
- 获取过滤器集类
- """
- filterset_class = getattr(view, "filterset_class", None)
- filterset_fields = getattr(view, "filterset_fields", None)
- if filterset_class:
- filterset_model = filterset_class._meta.model
- if filterset_model and queryset is not None:
- assert issubclass(
- queryset.model, filterset_model
- ), "FilterSet model %s does not match queryset model %s" % (
- filterset_model,
- queryset.model,
- )
- return filterset_class
- if filterset_fields and queryset is not None:
- MetaBase = getattr(self.filterset_base, "Meta", object)
- # 自动创建过滤器集类
- class AutoFilterSet(self.filterset_base):
- class Meta(MetaBase):
- model = queryset.model
- fields = filterset_fields
- return AutoFilterSet
- return None
- def get_filterset_kwargs(self, request, queryset, view):
- """
- 获取过滤器集参数
- """
- return {
- "data": request.query_params,
- "queryset": queryset,
- "request": request,
- }
- def filter_queryset(self, request, queryset, view):
- """
- 过滤查询集
- """
- filterset = self.get_filterset(request, queryset, view)
- if filterset is None:
- return queryset
- if not filterset.is_valid() and self.raise_exception:
- raise utils.translate_validation(filterset.errors)
- return filterset.qs
复制代码 6、使用示例
6.1 基本配置
- INSTALLED_APPS = [
- # ...
- 'rest_framework',
- 'django_filters',
- ]
- REST_FRAMEWORK = {
- 'DEFAULT_FILTER_BACKENDS': [
- 'django_filters.rest_framework.DjangoFilterBackend',
- 'rest_framework.filters.SearchFilter',
- 'rest_framework.filters.OrderingFilter',
- ]
- }
复制代码 6.2 SearchFilter
- class BookViewSet(viewsets.ModelViewSet):
- queryset = Book.objects.all()
- serializer_class = BookSerializer
- filter_backends = [SearchFilter]
- # 在下面这些字段中搜索search后面的关键字
- search_fields = ['title', 'author__name', 'categories__name']
-
- # 可以使用前缀指定搜索方式
- # search_fields = ['^title'] # 开头匹配
- # search_fields = ['=title'] # 精确匹配
- # search_fields = ['$title'] # 正则匹配
- # URLs
- # /books/?search=python+john 搜索同时包含 "python" 和 "john"
- # /books/?search=python john # 或
复制代码 6.3 OrderingFilter
- class BookViewSet(viewsets.ModelViewSet):
- queryset = Book.objects.all()
- serializer_class = BookSerializer
- filter_backends = [OrderingFilter]
- ordering_fields = ['title', 'publication_date', 'price']
- ordering = ['-publication_date'] # 默认排序
- # URLs
- # /books/?ordering=price # 按价格升序
- # /books/?ordering=-price # 按价格降序
- # /books/?ordering=publication_date,-price # 多字段排序
复制代码 6.4 DjangoFilterBackend
- from django_filters.rest_framework import DjangoFilterBackend
- class ProductViewSet(viewsets.ReadOnlyModelViewSet):
- queryset = Product.objects.all()
- serializer_class = ProductSerializer
- filter_backends = [DjangoFilterBackend]
- # 定义可过滤的字段
- filterset_fields = ['category', 'in_stock', 'price']
- #6.1 基本配置
- # GET /api/products/?category=electronics&in_stock=true
- # GET /api/products/?price=99.99
复制代码 6.5 组合使用多个过滤器
- class BookViewSet(viewsets.ModelViewSet):
- queryset = Book.objects.all()
- serializer_class = BookSerializer
- filter_backends = [
- DjangoFilterBackend,
- SearchFilter,
- OrderingFilter
- ]
- filterset_fields = ['author', 'is_published']
- search_fields = ['title', 'author__name']
- ordering_fields = ['title', 'publication_date', 'price']
- ordering = ['-publication_date']
- # URLs
- # /books/?author=1&is_published=true&search=python&ordering=-price
复制代码 来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除 |