You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 09:30:46