同时使用自定义Django FilterSet与OrderingFilter导致FilterSet失效
问题描述
自定义的MyFilterSet和CustomOrderingFilter单独使用均正常,但同时在视图中配置后,FilterSet完全失效。已移除无关业务逻辑,仅保留核心代码;尝试将OrderingFilter嵌入FilterSet中,问题仍未解决。(无错误提示,当前环境:Django 1.11.4、Python 2.7.17、PostgreSQL 10.23,暂时难以升级,但如果升级是可行方案也希望了解)
代码片段
filters.py
class MyFilterSet(FilterSet): id = filters.NumberFilter(name='id', lookup_expr='exact') class Meta: model = TheModel fields = list() exclude = [] class CustomOrderingFilter(OrderingFilter): fields_related = { 'vendor': { 'data_type': 'text', 'loc': 'thing__vendor', }, } def get_ordering(self, request, queryset, view): param = request.query_params.get(self.ordering_param) if param: return param return self.get_default_ordering(view) def filter_queryset(self, request, queryset, view): ordering = self.get_ordering(request, queryset, view) if ordering in self.fields_related.keys(): return queryset.order_by(ordering) return queryset
views.py
class getMyListAPIView(generics.ListAPIView): serializer_class = MySerializer filter_class = MyFilterSet filter_backends = [CustomOrderingFilter] def get_queryset(self): models = TheModel.objects return models
问题原因及解决方法
核心原因
在DRF中,当显式指定filter_backends时,会覆盖默认的过滤后端列表——默认的DjangoFilterBackend并不在你配置的[CustomOrderingFilter]中,导致MyFilterSet对应的过滤逻辑根本不会被执行。
解决方法1:补充默认过滤后端到filter_backends
修改视图类的filter_backends配置,加入DjangoFilterBackend,让FilterSet和自定义排序过滤器同时生效:
from rest_framework.filters import DjangoFilterBackend class getMyListAPIView(generics.ListAPIView): serializer_class = MySerializer filter_class = MyFilterSet filter_backends = [DjangoFilterBackend, CustomOrderingFilter] def get_queryset(self): return TheModel.objects.all() # 修正:返回查询集实例而非Manager对象
注意:原get_queryset中返回TheModel.objects是错误的,必须返回查询集实例(如all()),否则后续过滤/排序逻辑会报错。
解决方法2:修复自定义OrderingFilter的逻辑缺陷
你的CustomOrderingFilter存在两处逻辑问题:
get_ordering返回字符串而非列表,不符合DRF父类方法的预期,可能导致多字段排序失效;- 未正确处理自定义字段的映射(比如
vendor应该对应thing__vendor排序)。
修改后的CustomOrderingFilter代码:
class CustomOrderingFilter(OrderingFilter): fields_related = { 'vendor': { 'data_type': 'text', 'loc': 'thing__vendor', }, } def get_ordering(self, request, queryset, view): param = request.query_params.get(self.ordering_param) if param: return param.split(',') # 将排序参数拆分为列表,支持多字段排序 return self.get_default_ordering(view) def filter_queryset(self, request, queryset, view): ordering = self.get_ordering(request, queryset, view) if ordering: processed_ordering = [] for field in ordering: # 处理降序前缀 is_desc = field.startswith('-') field_name = field.lstrip('-') # 替换为自定义关联字段 if field_name in self.fields_related: target_field = self.fields_related[field_name]['loc'] processed_ordering.append(f'-{target_field}' if is_desc else target_field) else: processed_ordering.append(field) return queryset.order_by(*processed_ordering) return queryset
关于版本升级
Django 1.11和Python 2.7均已停止官方维护,存在安全漏洞风险。升级到Django 3.x+(搭配Python 3.6+)后,DRF的过滤后端逻辑更完善,能避免旧版本的兼容性问题,同时支持更多新特性。如果业务允许,建议优先规划升级。
内容的提问来源于stack exchange,提问作者ABoyBrushedRed

