如何在Django REST Framework中使用choices实现评分过滤?
解决Django REST Framework中保留choices同时实现rating字段过滤的问题
问题原因
你的rating字段在数据库中存储的是choices定义里的键值(比如'1'对应显示的'U'),而默认的DjangoFilterBackend会直接用你传入的过滤参数匹配数据库中的存储值。如果过滤时传入的是显示文本(比如U),就会和数据库里的'1'不匹配,导致无结果;如果传入的是键值(比如1),默认过滤其实是有效的。
解决方案
根据需求选择以下方案:
方案1:同时支持键值和显示文本过滤
自定义FilterSet,自动识别传入参数是键还是显示文本,对应匹配数据库值:
from django_filters import rest_framework as filters from .models import Film class FilmFilterSet(filters.FilterSet): rating = filters.ChoiceFilter(choices=Film.rating.field.choices, method='filter_rating') def filter_rating(self, queryset, name, value): # 检查传入值是否为choices的键 is_key = any(key == value for key, label in Film.rating.field.choices) if is_key: return queryset.filter(rating=value) # 匹配显示文本,找到对应数据库键 for key, label in Film.rating.field.choices: if label == value: return queryset.filter(rating=key) return queryset.none() class Meta: model = Film fields = ('rating',)
然后修改视图使用自定义FilterSet:
class FilmList(ListAPIView): queryset = Film.objects.all() serializer_class = FilmSerializer filter_backends = (DjangoFilterBackend,) filterset_class = FilmFilterSet # 替换为自定义类
方案2:仅支持显示文本过滤
如果只需要用户通过U/PG这类显示文本过滤,简化FilterSet:
from django_filters import rest_framework as filters from .models import Film class FilmFilterSet(filters.FilterSet): rating = filters.CharFilter(method='filter_rating_by_label') def filter_rating_by_label(self, queryset, name, value): rating_key = None for key, label in Film.rating.field.choices: if label == value: rating_key = key break return queryset.filter(rating=rating_key) if rating_key else queryset.none() class Meta: model = Film fields = ('rating',)
同样在视图中替换filterset_class为这个自定义类即可。
方案3:仅支持键值过滤(无需修改代码)
如果只需要用户传入1/2这类键值过滤,原代码本身是有效的,不需要做任何改动。
内容的提问来源于stack exchange,提问作者Cromwell1963
相关产品推荐
相关产品推荐

