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

DRF中使用StringRelatedField导致interest_category传值为null的问题

DRF中外键字段传值为null的问题及解决方案

问题场景

在Django Rest Framework中设置interest_category外键字段时,前端发送interest_category: 2,但数据库中该字段被存为null。移除序列化器中的serializers.StringRelatedField()后,传值恢复正常。

发送的数据

{
    "project_title": "Test",
    "project_description": "Test1",
    "interest_category": 2
}

数据库存储结果

(35, Test, Test1, 2022-09-22 02:11:54.342202+00, 2022-09-22 02:11:54.342202+00, 1, null)

相关代码

Serializer

class ProjectsSerializer(serializers.ModelSerializer):

    interest_category = serializers.StringRelatedField()

    class Meta:
        model = Project
        read_only_fields = (
            'created_by',
            'created_at',
            'updated_at',
        ),
        fields = (
            'project_title',
            'project_description',
            'interest_category',
        )

Model

class Project(models.Model):
    project_title = models.CharField(max_length=255)
    project_description = models.TextField(max_length=1500)
    interest_category = models.ForeignKey(Interests, on_delete=models.CASCADE)
    created_by = models.ForeignKey(User, related_name='projects', on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now_add=True)

Views

class ProjectViewSet(viewsets.ModelViewSet):
    serializer_class = ProjectsSerializer
    queryset = Project.objects.order_by('-created_at')
    
    def perform_create(self, serializer):
        serializer.save(created_by=self.request.user)

问题原因

serializers.StringRelatedField()是只读字段,它仅负责将外键关联的对象序列化为字符串(比如Interests模型__str__方法的返回值),完全不支持反序列化操作——DRF会直接忽略前端传入的该字段值,导致数据库中该字段被设为null。


解决方案:同时支持ID提交与名称返回

如果需要既能通过ID提交数据,又能在返回结果中拿到外键字段的名称,推荐以下几种方法:

方法1:新增只读字段返回名称

保留默认外键字段用于接收ID,新增一个只读的StringRelatedField返回名称:

class ProjectsSerializer(serializers.ModelSerializer):
    # 新增只读字段,返回interest_category的名称
    interest_category_name = serializers.StringRelatedField(source='interest_category', read_only=True)
    
    class Meta:
        model = Project
        read_only_fields = ('created_by', 'created_at', 'updated_at')
        fields = ('project_title', 'project_description', 'interest_category', 'interest_category_name')
  • 前端提交时依然传interest_category: 2
  • 接口返回时会同时包含interest_category(ID)和interest_category_name(对应名称)

方法2:重写to_representation方法

不想新增字段的话,可重写序列化器的to_representation方法,在返回数据中注入名称:

class ProjectsSerializer(serializers.ModelSerializer):
    class Meta:
        model = Project
        read_only_fields = ('created_by', 'created_at', 'updated_at')
        fields = ('project_title', 'project_description', 'interest_category')
    
    def to_representation(self, instance):
        # 获取默认序列化数据
        data = super().to_representation(instance)
        # 把外键名称添加到返回结果里
        data['interest_category_name'] = str(instance.interest_category)
        return data

效果与方法1一致,无需单独声明字段。

方法3:用SlugRelatedField支持名称提交(可选)

如果需求是前端通过名称而非ID提交数据,同时返回名称,可使用SlugRelatedField(假设Interests模型有name字段):

class ProjectsSerializer(serializers.ModelSerializer):
    interest_category = serializers.SlugRelatedField(
        queryset=Interests.objects.all(),
        slug_field='name'  # 指定用Interests的name字段匹配
    )
    
    class Meta:
        model = Project
        read_only_fields = ('created_by', 'created_at', 'updated_at')
        fields = ('project_title', 'project_description', 'interest_category')

这种场景下前端需要传interest_category: "分类名称",接口返回的也是该名称。


内容的提问来源于stack exchange,提问作者ckcAdmin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 15:10:25