找回密码
 立即注册
首页 业界区 业界 DRF过滤器

DRF过滤器

舒娅友 昨天 15:21
Django REST Framework 提供了强大的过滤功能,允许客户端通过查询参数对 API 结果进行过滤、搜索和排序。
1、过滤器工作流程

ListModelMixin的 list() -> GenericAPIView filter_queryset()
  1. # rest_framework/mixins.py
  2. class ListModelMixin:
  3.     def list(self, request, *args, **kwargs):
  4.         # 获取初始查询集
  5.         queryset = self.get_queryset()
  6.         
  7.         # 应用所有过滤器
  8.         queryset = self.filter_queryset(queryset)
  9.         
  10.         # 应用分页
  11.         page = self.paginate_queryset(queryset)
  12.         if page is not None:
  13.             serializer = self.get_serializer(page, many=True)
  14.             return self.get_paginated_response(serializer.data)
  15.         
  16.         # 序列化并返回
  17.         serializer = self.get_serializer(queryset, many=True)
  18.         return Response(serializer.data)
  19. # rest_framework/generics.py
  20. class GenericAPIView:
  21.     filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
  22.     def filter_queryset(self, queryset):
  23.         # 遍历所有过滤器后端
  24.         for backend in list(self.filter_backends):
  25.             # 调用各个过滤器的filter_queryset方法
  26.             queryset = backend().filter_queryset(self.request, queryset, self)
  27.         return queryset
复制代码
2、过滤器基类BaseFilterBackend
  1. class BaseFilterBackend:
  2.     """
  3.     所有过滤器后端的基类
  4.     """
  5.     def filter_queryset(self, request, queryset, view):
  6.         """
  7.         过滤查询集的主要方法,必须在子类中实现
  8.         """
  9.         raise NotImplementedError(".filter_queryset() must be overridden.")
  10.     def get_schema_fields(self, view):
  11.         """
  12.         返回 OpenAPI schema 字段
  13.         """
  14.         return []
  15.     def get_schema_operation_parameters(self, view):
  16.         """
  17.         返回 OpenAPI 操作参数
  18.         """
  19.         return []
复制代码
3、SearchFilter

SearchFilter使用 OR 逻辑连接多个字段的搜索条件
  1. class SearchFilter(BaseFilterBackend):
  2.     # 默认搜索参数名
  3.     search_param = api_settings.SEARCH_PARAM
  4.     template = 'rest_framework/filters/search.html'
  5.     lookup_prefixes = {
  6.         '^': 'istartswith',
  7.         '=': 'iexact',
  8.         '@': 'search',
  9.         '$': 'iregex',
  10.     }
  11.     search_title = _('Search')
  12.     search_description = _('A search term.')
  13.     def get_search_fields(self, view, request):
  14.         """从视图获取搜索字段列表"""
  15.         return getattr(view, 'search_fields', None)
  16.     def get_search_terms(self, request):
  17.         """解析查询参数中的搜索条件"""
  18.         value = request.query_params.get(self.search_param, '')
  19.         field = CharField(trim_whitespace=False, allow_blank=True)
  20.         cleaned_value = field.run_validation(value)
  21.         return search_smart_split(cleaned_value)
  22.     def construct_search(self, field_name, queryset):
  23.         """构建搜索查询条件"""
  24.         lookup = self.lookup_prefixes.get(field_name[0])
  25.         if lookup:
  26.             field_name = field_name[1:]
  27.         else:
  28.                         # ...
  29.             lookup = 'icontains'
  30.         return LOOKUP_SEP.join([field_name, lookup])
  31.     def filter_queryset(self, request, queryset, view):
  32.                 # 获取视图中定义的search_fields
  33.         search_fields = self.get_search_fields(view, request)
  34.         search_terms = self.get_search_terms(request)
  35.         if not search_fields or not search_terms:
  36.             return queryset
  37.                
  38.                 # 构建ORM查询条件
  39.         orm_lookups = [
  40.             self.construct_search(str(search_field), queryset)
  41.             for search_field in search_fields
  42.         ]
  43.         base = queryset
  44.         conditions = (
  45.             reduce(
  46.                 operator.or_,
  47.                 (models.Q(**{orm_lookup: term}) for orm_lookup in orm_lookups)
  48.             ) for term in search_terms
  49.         )
  50.         queryset = queryset.filter(reduce(operator.and_, conditions))
  51.         # 如果设置了distinct,确保结果去重
  52.         if self.must_call_distinct(queryset, search_fields):
  53.             queryset = queryset.filter(pk=models.OuterRef('pk'))
  54.             queryset = base.filter(models.Exists(queryset))
  55.         return queryset
  56.     def get_schema_fields(self, view):
  57.             """
  58.         返回 OpenAPI schema 字段
  59.         """
  60.         return [
  61.             coreapi.Field(
  62.                 name=self.search_param,
  63.                 required=False,
  64.                 location='query',
  65.                 schema=coreschema.String(
  66.                     title=force_str(self.search_title),
  67.                     description=force_str(self.search_description)
  68.                 )
  69.             )
  70.         ]
复制代码
4、OrderingFilter

按指定字段排序,支持多字段和反向排序
  1. class OrderingFilter(BaseFilterBackend):
  2.     # 默认查询参数名
  3.     ordering_param = api_settings.ORDERING_PARAM
  4.     # 默认排序字段
  5.     ordering_fields = None
  6.    
  7.     # 默认排序方向
  8.     ordering_title = _('Ordering')
  9.     def get_ordering(self, request, queryset, view):
  10.         """
  11.         从请求中解析排序参数
  12.         """
  13.         params = request.query_params.get(self.ordering_param)
  14.         if params:
  15.             # 分割多个排序字段
  16.             fields = [param.strip() for param in params.split(',')]
  17.             # 过滤无效字段
  18.             ordering = self.remove_invalid_fields(queryset, fields, view, request)
  19.             if ordering:
  20.                 return ordering
  21.         # 返回默认排序
  22.         return self.get_default_ordering(view)
  23.     def get_default_ordering(self, view):
  24.         """
  25.         获取默认排序设置
  26.         """
  27.         ordering = getattr(view, 'ordering', None)
  28.         if isinstance(ordering, str):
  29.             return (ordering,)
  30.         return ordering
  31.    
  32.     def filter_queryset(self, request, queryset, view):
  33.         """
  34.         应用排序到查询集
  35.         """
  36.         ordering = self.get_ordering(request, queryset, view)
  37.         if ordering:
  38.             return queryset.order_by(*ordering)
  39.         return queryset
复制代码
5、DjangoFilterBackend

DjangoFilterBackend基于 django-filter库,提供了更强大的字段过滤能力
  1. class DjangoFilterBackend:
  2.     # 过滤器基类
  3.     filterset_base = filterset.FilterSet
  4.     # 验证失败时是否引发异常
  5.     raise_exception = True
  6.     def get_filterset(self, request, queryset, view):
  7.         """
  8.         获取过滤器集
  9.         """
  10.         filterset_class = self.get_filterset_class(view, queryset)
  11.         if filterset_class is None:
  12.             return None
  13.         kwargs = self.get_filterset_kwargs(request, queryset, view)
  14.         return filterset_class(**kwargs)
  15.     def get_filterset_class(self, view, queryset=None):
  16.         """
  17.         获取过滤器集类
  18.         """
  19.         filterset_class = getattr(view, "filterset_class", None)
  20.         filterset_fields = getattr(view, "filterset_fields", None)
  21.         if filterset_class:
  22.             filterset_model = filterset_class._meta.model
  23.             if filterset_model and queryset is not None:
  24.                 assert issubclass(
  25.                     queryset.model, filterset_model
  26.                 ), "FilterSet model %s does not match queryset model %s" % (
  27.                     filterset_model,
  28.                     queryset.model,
  29.                 )
  30.             return filterset_class
  31.         if filterset_fields and queryset is not None:
  32.             MetaBase = getattr(self.filterset_base, "Meta", object)
  33.             # 自动创建过滤器集类
  34.             class AutoFilterSet(self.filterset_base):
  35.                 class Meta(MetaBase):
  36.                     model = queryset.model
  37.                     fields = filterset_fields
  38.             return AutoFilterSet
  39.         return None
  40.     def get_filterset_kwargs(self, request, queryset, view):
  41.         """
  42.         获取过滤器集参数
  43.         """
  44.         return {
  45.             "data": request.query_params,
  46.             "queryset": queryset,
  47.             "request": request,
  48.         }
  49.     def filter_queryset(self, request, queryset, view):
  50.         """
  51.         过滤查询集
  52.         """
  53.         filterset = self.get_filterset(request, queryset, view)
  54.         if filterset is None:
  55.             return queryset
  56.         if not filterset.is_valid() and self.raise_exception:
  57.             raise utils.translate_validation(filterset.errors)
  58.         return filterset.qs
复制代码
6、使用示例

6.1 基本配置
  1. INSTALLED_APPS = [
  2.     # ...
  3.     'rest_framework',
  4.     'django_filters',
  5. ]
  6. REST_FRAMEWORK = {
  7.     'DEFAULT_FILTER_BACKENDS': [
  8.         'django_filters.rest_framework.DjangoFilterBackend',
  9.         'rest_framework.filters.SearchFilter',
  10.         'rest_framework.filters.OrderingFilter',
  11.     ]
  12. }
复制代码
6.2 SearchFilter
  1. class BookViewSet(viewsets.ModelViewSet):
  2.     queryset = Book.objects.all()
  3.     serializer_class = BookSerializer
  4.     filter_backends = [SearchFilter]
  5.     # 在下面这些字段中搜索search后面的关键字
  6.     search_fields = ['title', 'author__name', 'categories__name']
  7.    
  8.     # 可以使用前缀指定搜索方式
  9.     # search_fields = ['^title']  # 开头匹配
  10.     # search_fields = ['=title']  # 精确匹配
  11.     # search_fields = ['$title']  # 正则匹配
  12. # URLs
  13. # /books/?search=python+john 搜索同时包含 "python" 和 "john"
  14. # /books/?search=python john  # 或
复制代码
6.3 OrderingFilter
  1. class BookViewSet(viewsets.ModelViewSet):
  2.     queryset = Book.objects.all()
  3.     serializer_class = BookSerializer
  4.     filter_backends = [OrderingFilter]
  5.     ordering_fields = ['title', 'publication_date', 'price']
  6.     ordering = ['-publication_date']  # 默认排序
  7. # URLs
  8. # /books/?ordering=price  # 按价格升序
  9. # /books/?ordering=-price  # 按价格降序
  10. # /books/?ordering=publication_date,-price  # 多字段排序
复制代码
6.4 DjangoFilterBackend
  1. from django_filters.rest_framework import DjangoFilterBackend
  2. class ProductViewSet(viewsets.ReadOnlyModelViewSet):
  3.     queryset = Product.objects.all()
  4.     serializer_class = ProductSerializer
  5.     filter_backends = [DjangoFilterBackend]
  6.     # 定义可过滤的字段
  7.     filterset_fields = ['category', 'in_stock', 'price']
  8. #6.1 基本配置
  9. # GET /api/products/?category=electronics&in_stock=true
  10. # GET /api/products/?price=99.99
复制代码
6.5 组合使用多个过滤器
  1. class BookViewSet(viewsets.ModelViewSet):
  2.     queryset = Book.objects.all()
  3.     serializer_class = BookSerializer
  4.     filter_backends = [
  5.         DjangoFilterBackend,
  6.         SearchFilter,
  7.         OrderingFilter
  8.     ]
  9.     filterset_fields = ['author', 'is_published']
  10.     search_fields = ['title', 'author__name']
  11.     ordering_fields = ['title', 'publication_date', 'price']
  12.     ordering = ['-publication_date']
  13. # URLs
  14. # /books/?author=1&is_published=true&search=python&ordering=-price
复制代码
来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除

相关推荐

您需要登录后才可以回帖 登录 | 立即注册