Django与django-filter Choice字段过滤报错:'super'无_set_choices属性
Django REST Framework中django-filter处理Choice字段报错的解决方案
问题复现
使用django-filter的ChoiceFilter处理Django模型中带choices的CharField(如source和status字段)时,触发以下错误:
AttributeError: 'super' object has no attribute '_set_choices'
改用CharFilter虽能正常运行,但无法实现基于选项值的精确过滤,不符合业务需求。
核心原因
- 参数误用:
ChoiceFilter默认基于选项值做精确匹配,不需要指定icontains这类模糊查找参数,该参数会干扰ChoiceFilter内部的字段初始化逻辑,导致继承链方法调用出错。 - 版本兼容问题:
django-filter与Django版本不匹配时,会出现这类底层方法调用的兼容性异常。
解决方案
方案1:简化ChoiceFilter定义,移除冲突参数
模型字段已定义choices,FilterSet会自动读取模型字段的选项配置,无需手动传入choices,同时去掉引发冲突的lookup_expr:
class IncidentFilter(django_filters.FilterSet): description = django_filters.CharFilter(lookup_expr='icontains') declarant_phone = django_filters.CharFilter(field_name='declarant__phone', lookup_expr='icontains') incident_address = django_filters.CharFilter(field_name='address__full_address', lookup_expr='icontains') # 简化ChoiceFilter定义 source = django_filters.ChoiceFilter() status = django_filters.ChoiceFilter() service = django_filters.Filter(method='filter_service') address_caller = django_filters.CharFilter(field_name='caller__address', lookup_expr='icontains') victims = django_filters.BooleanFilter(method='filter_victims') death = django_filters.BooleanFilter(method='filter_death') dt_create = django_filters.Filter(method='filter_dt_create') incident_moment = django_filters.Filter(method='filter_incident_moment') class Meta: model = models.Incident fields = [ 'is_needs_reaction', 'is_social_significant', 'is_risk_emergency', 'description', 'declarant_phone', 'status', 'source', 'operator', 'incident_address', 'kind', 'service', 'address_caller', 'victims', 'death', 'dt_create', ]
方案2:修复版本兼容性
查看django-filter与Django的版本对应关系,调整到兼容版本:
- Django 4.x 对应 django-filter 23.x+
- Django 3.x 对应 django-filter 2.x-22.x
- Django 2.x 对应 django-filter 1.x-2.x
通过pip调整版本:
# 升级到最新兼容版本 pip install --upgrade django-filter # 或指定具体兼容版本 pip install django-filter==23.5
方案3:自定义方法实现选项文本模糊匹配
如果需要基于选项的显示文本(而非数据库存储值)做模糊查找,可自定义方法过滤器:
class IncidentFilter(django_filters.FilterSet): # 新增基于文本的模糊过滤字段 source_text = django_filters.CharFilter(method='filter_source_text') status_text = django_filters.CharFilter(method='filter_status_text') def filter_source_text(self, queryset, name, value): # 反向匹配文本对应的存储值 source_choices = dict(consts.IncidentSource.CHOICES) matching_values = [k for k, v in source_choices.items() if value.lower() in v.lower()] return queryset.filter(source__in=matching_values) def filter_status_text(self, queryset, name, value): status_choices = dict(consts.IncidentStatus.CHOICES) matching_values = [k for k, v in status_choices.items() if value.lower() in v.lower()] return queryset.filter(status__in=matching_values) # 其他过滤器和Meta类保持不变
验证
修改后重新运行测试或接口请求,确认:
- 使用
source/status参数能精确匹配选项存储值 - 无
AttributeError报错
内容的提问来源于stack exchange,提问作者Nik Vozakssar
相关产品推荐
相关产品推荐

