如何为ViewSet的自定义action设置django-filter的FilterSet
Django ViewSet自定义Action接入django-filter的实现方案
首先完成基础配置:
- 安装依赖:
pip install django-filter djangorestframework - 在项目配置文件中注册app并配置默认过滤后端:
# settings.py INSTALLED_APPS = [ # 其他内置/自定义应用 'rest_framework', 'django_filters', ] REST_FRAMEWORK = { 'DEFAULT_FILTER_BACKENDS': [ 'django_filters.rest_framework.DjangoFilterBackend', # 可按需追加搜索、排序等其他过滤后端 ] }
你可以根据代码习惯选择以下两种生效稳定的实现方式:
方案1:手动实例化FilterSet(全版本兼容,逻辑可控)
这种方式不受DRF版本、ViewSet继承链影响,适合复杂业务场景。只需要在自定义action逻辑中,将待过滤的queryset、请求参数传入FilterSet,校验通过后取过滤结果即可:
from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response from .models import Product from .filters import ProductFilter # 你自定义的FilterSet类 from .serializers import ProductSerializer class ProductViewSet(viewsets.ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer filterset_class = ProductFilter # 内置list接口默认使用的过滤类 # 示例自定义action:返回所有在售商品,支持ProductFilter的所有过滤规则 @action(detail=False, methods=['get']) def on_sale(self, request): # 1. 拿到当前action对应的原始queryset base_qs = Product.objects.filter(is_on_sale=True) # 2. 实例化过滤器,传入请求参数、原始queryset filter_set = ProductFilter( data=request.query_params, queryset=base_qs, request=request ) # 3. 校验过滤参数合法性 if not filter_set.is_valid(): return Response(filter_set.errors, status=400) # 4. 取过滤后的结果,走正常的分页、序列化逻辑 filtered_qs = filter_set.qs page = self.paginate_queryset(filtered_qs) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(filtered_qs, many=True) return Response(serializer.data)
方案2:给Action绑定过滤配置(代码更简洁)
DRF的@action装饰器支持直接给单个action指定过滤后端和过滤类,配置后和内置的list接口逻辑完全一致,不需要手动编写FilterSet实例化代码:
from django_filters.rest_framework import DjangoFilterBackend from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response from .models import Product from .filters import ProductFilter from .serializers import ProductSerializer class ProductViewSet(viewsets.ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer filterset_class = ProductFilter @action( detail=False, methods=['get'], # 为当前action指定过滤后端 filter_backends=[DjangoFilterBackend], # 为当前action指定使用的FilterSet,可和全局配置不同 filterset_class=ProductFilter ) def on_sale(self, request): # 调用ViewSet自带的filter_queryset方法,自动完成过滤 base_qs = Product.objects.filter(is_on_sale=True) filtered_qs = self.filter_queryset(base_qs) # 后续分页、序列化逻辑和方案1完全一致 page = self.paginate_queryset(filtered_qs) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(filtered_qs, many=True) return Response(serializer.data)
注意事项
- 针对
detail=True的单资源操作action(路由格式为/products/{id}/xxx/),一般不需要过滤集合类queryset,如果需要过滤当前资源关联的子对象集合,把关联对象的queryset传入FilterSet即可,逻辑和上述示例一致。 - 如果多个自定义action共用同一个FilterSet,可以把FilterSet定义为ViewSet的类属性,action内直接通过
self.filterset_class引用即可,不需要重复导入或传参。 - 不要在DRF视图中混用原生Django的Filter后端,必须使用
django_filters.rest_framework.DjangoFilterBackend,否则会出现响应格式不兼容、分页失效等问题。
内容的提问来源于stack exchange,提问作者Seofale
相关产品推荐
相关产品推荐

