DRF过滤器
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 queryset2、过滤器基类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)
if lookup:
field_name = field_name
else:
# ...
lookup = 'icontains'
return LOOKUP_SEP.join()
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 =
# 过滤无效字段
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 queryset5、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.qs6、使用示例
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 =
# 在下面这些字段中搜索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 =
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 =
# 定义可过滤的字段
filterset_fields = ['category', 'in_stock', 'price']
#6.1 基本配置
# GET /api/products/?category=electronics&in_stock=true
# GET /api/products/?price=99.996.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
来源:豆瓜网用户自行投稿发布,如果侵权,请联系站长删除
页:
[1]