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

Django DRF嵌套序列化器无法保存模型:求问题排查与解决

Django DRF嵌套序列化器保存失败问题排查

问题概述

使用Django DRF嵌套序列化器时出现保存失败,报错信息:NOT NULL constraint failed: VideoMaker_videomaker.duration_id,无法完成模型数据持久化。相关代码、错误信息及期望返回结果如下:

模型代码

class Duration(models.Model):
    start_time = models.PositiveIntegerField(blank=True,null=True)
    end_time = models.PositiveIntegerField(blank=True,null=True)

    def __str__(self):
        self.end_time


class VideoMaker(models.Model):
    CHOICE_SELECT = [
        ('vo','voice over'),
        ('bg_music','backgournd music'),
        ('video_music','vidio music'),
    ]
    typee = models.CharField(max_length=15,choices=CHOICE_SELECT)
    high_volume = models.PositiveIntegerField()
    low_volume = models.PositiveIntegerField()
    video_component_id = models.CharField(max_length=300,null=True,blank=True)
    url = models.URLField(blank=True,null=True)
    duration = models.ForeignKey(Duration, on_delete=models.CASCADE)

    def __str__(self):
        return self.id

序列化器代码

class DuratinSeriaizer(serializers.ModelSerializer):
    class Meta:
        model = Duration
        fields = [
            'id',
            'start_time',
            'end_time',
        ]


class VideoMakerSerializer(serializers.ModelSerializer):
    duration = DuratinSeriaizer()
    class Meta:
        model = VideoMaker
        fields = [
            'id',
            'typee',
            'high_volume',
            'low_volume',
            'video_component_id',
            'url',
            'duration',
        ]

    def create(self, validated_data):
        datas = validated_data.pop('duration')
        duration_data = VideoMaker.objects.create(**validated_data)
        for datas in datas:
            Duration.objects.create(duration=duration_data,**datas)
        Duration.save()
        return duration_data

视图代码

class AudioCreateApiView(generics.ListCreateAPIView):
    queryset = VideoMaker.objects.all()
    serializer_class = VideoMakerSerializer

期望返回结果

{
    "id": 6,
    "typee": "vo",
    "high_volume": 65,
    "low_volume": 123,
    "video_component_id": "wertyuio",
    "url": "",
    "duration": {
        "id": 9,
        "start_time": 21,
        "end_time": 98
    }
}

问题根源

  1. 外键依赖逻辑颠倒:VideoMaker的duration是必填外键,原代码先创建VideoMaker实例,此时未关联任何Duration,触发NOT NULL约束错误。
  2. 代码逻辑错误:
    • 错误将duration的单条字典数据当成列表循环处理
    • 错误调用类方法Duration.save(),应该操作实例的save()方法
    • 关联字段名称错误:Duration模型不存在duration字段,无需反向关联

修正方案

1. 修正模型代码(修复__str__方法的语法问题)

class Duration(models.Model):
    start_time = models.PositiveIntegerField(blank=True, null=True)
    end_time = models.PositiveIntegerField(blank=True, null=True)

    def __str__(self):
        return str(self.end_time)  # 补充return并转为字符串类型


class VideoMaker(models.Model):
    CHOICE_SELECT = [
        ('vo', 'voice over'),
        ('bg_music', 'backgournd music'),
        ('video_music', 'vidio music'),
    ]
    typee = models.CharField(max_length=15, choices=CHOICE_SELECT)
    high_volume = models.PositiveIntegerField()
    low_volume = models.PositiveIntegerField()
    video_component_id = models.CharField(max_length=300, null=True, blank=True)
    url = models.URLField(blank=True, null=True)
    duration = models.ForeignKey(Duration, on_delete=models.CASCADE)

    def __str__(self):
        return str(self.id)  # 整数转字符串,符合__str__返回要求

2. 核心修正序列化器代码(调整创建顺序)

class DurationSerializer(serializers.ModelSerializer):  # 修正拼写错误DuratinSeriaizer
    class Meta:
        model = Duration
        fields = ['id', 'start_time', 'end_time']


class VideoMakerSerializer(serializers.ModelSerializer):
    duration = DurationSerializer()

    class Meta:
        model = VideoMaker
        fields = ['id', 'typee', 'high_volume', 'low_volume', 'video_component_id', 'url', 'duration']

    def create(self, validated_data):
        # 先取出嵌套的duration数据
        duration_data = validated_data.pop('duration')
        # 先创建Duration实例
        duration_instance = Duration.objects.create(**duration_data)
        # 创建VideoMaker时关联已生成的Duration实例
        videomaker_instance = VideoMaker.objects.create(duration=duration_instance, **validated_data)
        return videomaker_instance

3. 视图代码无需修改

保持原AudioCreateApiView代码即可。


验证效果

发送如下请求体:

{
    "typee": "vo",
    "high_volume": 65,
    "low_volume": 123,
    "video_component_id": "wertyuio",
    "url": "",
    "duration": {
        "start_time": 21,
        "end_time": 98
    }
}

接口将返回符合期望的嵌套结构数据,且数据成功写入数据库。

内容的提问来源于stack exchange,提问作者M. Roshan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 08:22:06