DRF过滤器

Django REST Framework 提供了强大的过滤功能,允许客户端通过查询参数对 API 结果进行过滤、搜索和排序。

1、过滤器工作流程

2、过滤器基类BaseFilterBackend

python 复制代码
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 逻辑连接多个字段的搜索条件

python 复制代码
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

按指定字段排序,支持多字段和反向排序

python 复制代码
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库,提供了更强大的字段过滤能力

python 复制代码
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 基本配置
python 复制代码
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
python 复制代码
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
python 复制代码
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
python 复制代码
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 组合使用多个过滤器
python 复制代码
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
相关推荐
该用户已不存在21 分钟前
PHP、Python、Node.js,谁能称霸2025?
python·node.js·php
luckys.one22 分钟前
第12篇|[特殊字符] Freqtrade 交易所接入全解:API、WebSocket、限频配置详解
网络·ide·python·websocket·网络协议·flask·流量运营
Pocker_Spades_A37 分钟前
Python快速入门专业版(四十六):Python类的方法:实例方法、类方法、静态方法与魔术方法
开发语言·python
cRack_cLick1 小时前
pandas库学习02——基本数据清洗
python·pandas
yubo05091 小时前
YOLO系列——实时屏幕检测
开发语言·windows·python
weixin_457340211 小时前
RTX5060 Ti显卡安装cuda版本PyTorch踩坑记录
人工智能·pytorch·python
测试19982 小时前
Web自动化测试之测试用例流程设计
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例
Juchecar3 小时前
示例说明 Flask 调试模式的安全隐患
python
大翻哥哥3 小时前
Python 2025:数据分析平台智能化转型与新范式
人工智能·python·数据分析
love530love4 小时前
EPGF 架构为什么能保持长效和稳定?
运维·开发语言·人工智能·windows·python·架构·系统架构