Django ManyToManyField插入异常:外键约束失败及标签字段为空
Django DRF多对多关联插入数据时外键约束错误及标签为空问题解决
问题描述
在Django项目中,Assignment模型与Tag模型通过ManyToManyField关联,使用Django REST Framework序列化器插入数据时出现两个问题:
- 标签字段返回空列表
[] - 触发
django.db.utils.IntegrityError: FOREIGN KEY constraint failed错误
相关代码及请求数据
models.py
from django.db import models class Tag(models.Model): """Tag for filtering Assignments.""" name = models.CharField(max_length=255, unique=True) def __str__(self): return self.name class Assignment(models.Model): """Models for assigment""" name = models.CharField(max_length=255) tags = models.ManyToManyField(Tag, blank=True) assignment_data = models.JSONField(blank=True) last_updated_by = models.CharField(max_length=255, blank=True) def __str__(self): return self.name
serializers.py
from rest_framework import serializers from assignment.models import ( Assignment, Tag, ) class TagSerializer(serializers.ModelSerializer): """Serializer for tags.""" class Meta: model = Tag fields = ["name"] class AssignmentSerializer(serializers.ModelSerializer): """Serializer for Assignment.""" tags = TagSerializer(many=True, required=False) class Meta: model = Assignment fields = ["id", "name", "tags", "last_updated_by", "assignment_data"] read_only_fields = ["id"] def create(self, validated_data): """Create an Assignment.""" tags_data = validated_data.pop("tags", []) instance = Assignment.objects.create(**validated_data) for tag_name in tags_data: created = Tag.objects.get_or_create(name=tag_name) instance.tags.set(tags) instance.save() return instance
请求数据
{ "name": "demo assignment", "tags": ["demo"], "last_updated_by": "user1", "assignment_data": { "hello": "Hello world 2" } }
错误信息
File "D:\Assignments-Api\.venv\Lib\site-packages\django\db\backends\base\base.py", line 313, in _commit return self.connection.commit() ^^^^^^^^^^^^^^^^^^^^^^^^ django.db.utils.IntegrityError: FOREIGN KEY constraint failed
错误分析
- 嵌套序列化器导致数据格式不匹配:因为AssignmentSerializer中tags字段使用了
TagSerializer嵌套,validated_data里的tags实际是字典列表(如[{"name": "demo"}]),而非请求中的字符串列表["demo"],直接将字典传给Tag.objects.get_or_create(name=tag_name)会导致类型错误,进而触发外键约束异常。 - create方法逻辑错误:
Tag.objects.get_or_create()返回的是元组(tag实例, 是否创建),原代码未正确提取Tag实例- 调用
instance.tags.set(tags)时,tags变量未定义 - 在循环内多次调用
set()方法不合理,应该收集所有Tag实例后一次性关联
修复方案
修改AssignmentSerializer的create方法,正确处理嵌套序列化的tags数据,并修复关联逻辑:
修复后的serializers.py
from rest_framework import serializers from assignment.models import ( Assignment, Tag, ) class TagSerializer(serializers.ModelSerializer): """Serializer for tags.""" class Meta: model = Tag fields = ["name"] class AssignmentSerializer(serializers.ModelSerializer): """Serializer for Assignment.""" tags = TagSerializer(many=True, required=False) class Meta: model = Assignment fields = ["id", "name", "tags", "last_updated_by", "assignment_data"] read_only_fields = ["id"] def create(self, validated_data): """Create an Assignment.""" tags_data = validated_data.pop("tags", []) instance = Assignment.objects.create(**validated_data) # 收集所有要关联的Tag实例 tag_instances = [] for tag_item in tags_data: # 从嵌套字典中取出name值 tag_name = tag_item.get("name") if tag_name: # 提取get_or_create返回的Tag实例 tag_instance, _ = Tag.objects.get_or_create(name=tag_name) tag_instances.append(tag_instance) # 一次性关联所有Tag实例 if tag_instances: instance.tags.set(tag_instances) return instance
简化方案(无需嵌套返回标签详情)
如果不需要返回标签的嵌套详情,仅通过字符串列表传递标签名称,可以改用StringRelatedField,代码更简洁:
class AssignmentSerializer(serializers.ModelSerializer): tags = serializers.StringRelatedField(many=True, required=False) class Meta: model = Assignment fields = ["id", "name", "tags", "last_updated_by", "assignment_data"] read_only_fields = ["id"] def create(self, validated_data): tags_data = validated_data.pop("tags", []) instance = Assignment.objects.create(**validated_data) tag_instances = [] for tag_name in tags_data: tag_instance, _ = Tag.objects.get_or_create(name=tag_name) tag_instances.append(tag_instance) instance.tags.set(tag_instances) return instance
此方案下请求数据保持原格式即可,StringRelatedField会自动处理字符串与Tag名称的映射。
内容的提问来源于stack exchange,提问作者Vivek Kumar
相关产品推荐
相关产品推荐

