Django中创建对象后关联外键字段的实现问题
问题概述
我需要处理包含description和choices字段的JSON输入,创建Question及关联的Choice记录。Choice模型通过外键关联Question,关联名称为choices,但JSON里的Choice没有question_id字段,必须在Question创建完成后再关联。尝试过程中遇到了Direct assignment to the forward side、Direct assignment to the reverse side of a related set is prohibited等错误,且不能修改关联名称和JSON输入字段。
原序列化器代码:
class CreateQuestionSerializer(serializers.ModelSerializer): description = serializers.CharField(max_length=1024, allow_blank=False, required=True) choices = CreateChoiceSerializer(many=True, required=True) def create(self, validated_data): choices = validated_data.pop('choices') question = Question.objects.create(**validated_data) choices_serializer = CreateChoiceSerializer(data=choices, many=True, context={'question': question}) if choices_serializer.is_valid(): choices_serializer.save() return question class CreateChoiceSerializer(serializers.ModelSerializer): description = serializers.CharField(max_length=1024, allow_blank=False, required=True, allow_null=False) class Meta: model = Choice fields = ['description'] def create(self, validated_data): question = self.context.get('question') choice = Choice.objects.create(question=question, **validated_data) return choice
原模型代码:
class Question(models.Model): id = models.AutoField(primary_key=True) description = models.CharField(max_length=1024, blank=False, null=False) class Choice(models.Model): id = models.AutoField(primary_key=True) question = models.ForeignKey(Question, related_name='choices', on_delete=models.PROTECT) description = models.CharField(max_length=1024, blank=False, null=False)
错误原因
- 原代码里
CreateQuestionSerializer的create方法存在缩进错误(if choices_serializer.is_valid():这一行缩进不对),导致代码逻辑异常。 - 使用嵌套序列化器批量保存时,
ModelSerializer的批量处理会触发反向关联集合的直接赋值限制,从而抛出Direct assignment to the reverse side of a related set is prohibited错误。
修复方案
方案一:用bulk_create直接批量创建(推荐)
这种方式跳过嵌套序列化器的复杂处理,直接通过Question实例批量创建关联的Choice,代码更简洁高效:
class CreateQuestionSerializer(serializers.ModelSerializer): description = serializers.CharField(max_length=1024, allow_blank=False, required=True) choices = CreateChoiceSerializer(many=True, required=True) class Meta: model = Question fields = ['description', 'choices'] def create(self, validated_data): choices_data = validated_data.pop('choices') question = Question.objects.create(**validated_data) # 批量创建Choice,直接关联已创建的Question Choice.objects.bulk_create( [Choice(question=question, **data) for data in choices_data] ) return question class CreateChoiceSerializer(serializers.ModelSerializer): description = serializers.CharField(max_length=1024, allow_blank=False, required=True, allow_null=False) class Meta: model = Choice fields = ['description']
方案二:保留嵌套序列化器,手动逐个创建
如果一定要用嵌套序列化器处理,可以手动遍历每个Choice数据,逐个验证保存,避免批量处理的问题:
class CreateQuestionSerializer(serializers.ModelSerializer): description = serializers.CharField(max_length=1024, allow_blank=False, required=True) choices = CreateChoiceSerializer(many=True, required=True) class Meta: model = Question fields = ['description', 'choices'] def create(self, validated_data): choices_data = validated_data.pop('choices') question = Question.objects.create(**validated_data) # 逐个处理Choice数据,确保每个都正确关联Question for data in choices_data: serializer = CreateChoiceSerializer(data=data, context={'question': question}) serializer.is_valid(raise_exception=True) serializer.save() return question class CreateChoiceSerializer(serializers.ModelSerializer): description = serializers.CharField(max_length=1024, allow_blank=False, required=True, allow_null=False) class Meta: model = Choice fields = ['description'] def create(self, validated_data): question = self.context.get('question') return Choice.objects.create(question=question, **validated_data)
关键注意点
- 方案一的
bulk_create性能更好,适合数据量较大的场景,但不会触发Choice模型的save信号,如果有信号逻辑需要处理,建议用方案二。 - 必须确保
Question实例创建完成后,再去创建关联的Choice,避免外键关联失败。
内容的提问来源于stack exchange,提问作者Akari Oozora
相关产品推荐
相关产品推荐

