Django DRF嵌套序列化报错:无法将OrderedDict赋值为Period实例
问题解决:嵌套序列化保存时的实例赋值错误
报错根源
报错ValueError: Cannot assign "OrderedDict([('instant', '2020-09-26')])": "Cashcashequivalentsrestrictedcashandrestrictedcashequivalents.period" must be a "Period" instance的核心原因有三点:
- 模型关联关系倒置:
Period模型的OneToOneField指向了Cashcashequivalentsrestrictedcashandrestrictedcashequivalents,但实际业务逻辑应该是后者包含一个指向Period的一对一关联(每个现金条目对应一个时间周期)。 - 序列化器
create方法逻辑错误:CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalentsSerializer的create方法中,创建现金条目时错误传入了period_data,且混淆了模型间的关联字段名。 - 顶层序列化器绕开嵌套处理:
CashFlowSerializer的create方法直接调用模型create处理嵌套数据,未触发嵌套序列化器对period字段的解析逻辑。
分步修复
1. 修正模型关联关系
调整模型间的一对一关联方向,让现金条目拥有指向Period的字段:
class Basetable(models.Model): basetable = models.AutoField(primary_key=True) CompanyId = models.IntegerField() class Cashcashequivalentsrestrictedcashandrestrictedcashequivalents(models.Model): cashtable = models.AutoField(primary_key=True) unitRef = models.CharField(max_length=100) value = models.CharField(max_length=100) decimals = models.CharField(max_length=100) basetable_id = models.ForeignKey(Basetable, on_delete=models.CASCADE) # 添加指向Period的一对一字段 period = models.OneToOneField(Period, on_delete=models.CASCADE, related_name='cash_entry') class Period(models.Model): period = models.AutoField(primary_key=True) # 改为blank=True,支持字段可选 instant = models.CharField(blank=True, max_length=100, null=True) startDate = models.CharField(blank=True, max_length=255, null=True) endDate = models.CharField(blank=True, max_length=255, null=True)
2. 修正嵌套序列化器的create方法
调整CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalentsSerializer的逻辑,正确创建并关联Period实例:
class PeriodSerializer(serializers.ModelSerializer): class Meta: model = Period fields = ['instant', 'startDate', 'endDate'] class CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalentsSerializer(serializers.ModelSerializer): period = PeriodSerializer(many=False) class Meta: model = Cashcashequivalentsrestrictedcashandrestrictedcashequivalents fields = ['decimals', 'unitRef', 'value', 'period'] def create(self, validated_data): period_data = validated_data.pop('period') # 创建Period实例 period = Period.objects.create(**period_data) # 创建现金条目并关联period cash_entry = Cashcashequivalentsrestrictedcashandrestrictedcashequivalents.objects.create(period=period, **validated_data) return cash_entry
3. 修正顶层序列化器的create方法
通过嵌套序列化器处理现金条目数据,确保触发嵌套字段的解析逻辑:
class CashFlowSerializer(serializers.ModelSerializer): # 指定source匹配模型的反向关联名称 CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalents = CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalentsSerializer( many=True, source='cashcashequivalentsrestrictedcashandrestrictedcashequivalents_set') class Meta: model = Basetable fields = ['CompanyId', 'CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalents'] def create(self, validated_data): cash_entries_data = validated_data.pop('cashcashequivalentsrestrictedcashandrestrictedcashequivalents_set') basetable = Basetable.objects.create(**validated_data) for entry_data in cash_entries_data: # 使用嵌套序列化器创建条目,自动处理period字段 serializer = CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalentsSerializer(data=entry_data) serializer.is_valid(raise_exception=True) serializer.save(basetable_id=basetable) return basetable
4. 视图代码无需修改
原视图的JSON数据格式符合要求,修复上述代码后,check.save()会正常触发嵌套序列化的保存逻辑。
验证结果
运行视图的GET请求后,数据库会正确生成:
- 1条
Basetable记录(对应CompanyId=320193) - 3条
Cashcashequivalentsrestrictedcashandrestrictedcashequivalents记录,分别关联对应的Period实例 - 3条
Period记录,各自保存对应的instant值
内容的提问来源于stack exchange,提问作者Maddy
相关产品推荐
相关产品推荐

