如何在Django Rest Framework的SlugRelatedField中按其他字段过滤?
问题描述
我有一个包含多个ForeignKey关联的模型,想要构建一个POST API,允许提交关联对象的可读名称而非id。这似乎适合使用SlugRelatedField,但该字段无法根据JSON请求中的其他字段过滤查询集。
具体需求:
- subdivision的查询集需根据organization字段过滤
- prediction_model的查询集需同时根据organization和subdivision字段过滤
组织名称唯一,SlugField可正常使用;但分部名称不唯一,必须按组织过滤;预测模型需同时按组织和分部过滤。我未找到在Django Rest Framework中便捷实现链式过滤的方法,且扩展SlugRelatedField类无法获取完整数据结构,请问有可行方案吗?
代码示例:
class GenericReportSerializer(serializers.ModelSerializer): """ GenericReportSerializer for POST. """ organization = serializers.SlugRelatedField(slug_field='name', queryset=models.Organization.objects.all()) subdivision = serializers.SlugRelatedField(slug_field='name', queryset=models.Subdivision.objects.all()) # queryset needs to be filtered by organization prediction_model = serializers.SlugRelatedField(slug_field='name', queryset=models.PredictionModel.objects.all()) # queryset needs to be filtered by both organization and subdivision class Meta: model = models.GenericReport fields = '__all__' # use all fields
可行解决方案
下面提供两种实用方案,均能实现根据请求中其他字段动态过滤SlugRelatedField的查询集。
方案一:自定义SlugRelatedField子类(通用型)
通过重写get_queryset方法,从请求上下文获取提交的字段值,针对性过滤查询集,适合多个序列化器复用:
from rest_framework import serializers from . import models class FilteredSlugRelatedField(serializers.SlugRelatedField): def get_queryset(self): queryset = super().get_queryset() request = self.context.get('request') if not request or not request.data: return queryset # 针对subdivision字段:按organization过滤 if self.field_name == 'subdivision': org_name = request.data.get('organization') if org_name: org = models.Organization.objects.get(name=org_name) return queryset.filter(organization=org) # 针对prediction_model字段:同时按organization和subdivision过滤 elif self.field_name == 'prediction_model': org_name = request.data.get('organization') sub_name = request.data.get('subdivision') if org_name and sub_name: org = models.Organization.objects.get(name=org_name) sub = models.Subdivision.objects.get(name=sub_name, organization=org) return queryset.filter(organization=org, subdivision=sub) return queryset class GenericReportSerializer(serializers.ModelSerializer): """ GenericReportSerializer for POST. """ organization = serializers.SlugRelatedField(slug_field='name', queryset=models.Organization.objects.all()) subdivision = FilteredSlugRelatedField(slug_field='name', queryset=models.Subdivision.objects.all()) prediction_model = FilteredSlugRelatedField(slug_field='name', queryset=models.PredictionModel.objects.all()) class Meta: model = models.GenericReport fields = '__all__'
方案二:在序列化器__init__方法中动态设置查询集(针对性)
直接在目标序列化器的初始化方法中,根据请求数据更新对应字段的查询集,逻辑更直观:
from rest_framework import serializers from . import models class GenericReportSerializer(serializers.ModelSerializer): """ GenericReportSerializer for POST. """ organization = serializers.SlugRelatedField(slug_field='name', queryset=models.Organization.objects.all()) subdivision = serializers.SlugRelatedField(slug_field='name', queryset=models.Subdivision.objects.all()) prediction_model = serializers.SlugRelatedField(slug_field='name', queryset=models.PredictionModel.objects.all()) class Meta: model = models.GenericReport fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) request = self.context.get('request') # 仅在POST请求时处理动态过滤 if request and request.method == 'POST' and request.data: # 过滤subdivision的查询集 org_name = request.data.get('organization') if org_name: try: org = models.Organization.objects.get(name=org_name) self.fields['subdivision'].queryset = self.fields['subdivision'].queryset.filter(organization=org) except models.Organization.DoesNotExist: pass # 后续验证会自动返回名称不存在的错误 # 过滤prediction_model的查询集 sub_name = request.data.get('subdivision') if org_name and sub_name: try: org = models.Organization.objects.get(name=org_name) sub = models.Subdivision.objects.get(name=sub_name, organization=org) self.fields['prediction_model'].queryset = self.fields['prediction_model'].queryset.filter(organization=org, subdivision=sub) except (models.Organization.DoesNotExist, models.Subdivision.DoesNotExist): pass
关键注意点
- 确保视图将
request传入序列化器的context,DRF默认视图已自动处理此逻辑。 - 异常无需额外捕获:如果提交的组织/分部名称不存在,DRF的默认验证流程会返回对应的错误提示。
- 字段顺序不影响:两种方案均直接从
request.data中取值,无需依赖请求字段的提交顺序。
内容的提问来源于stack exchange,提问作者Scott
相关产品推荐
相关产品推荐

