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 } }
问题根源
- 外键依赖逻辑颠倒:
VideoMaker的duration是必填外键,原代码先创建VideoMaker实例,此时未关联任何Duration,触发NOT NULL约束错误。 - 代码逻辑错误:
- 错误将
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
相关产品推荐
相关产品推荐

