DRF序列化器如何创建外键模型数据?报反向关联直接赋值错误
报错原因
该报错触发的核心原因是items属于ConsignmentNote模型的反向关联集合(即ConsignmentItem模型通过外键指向ConsignmentNote时设置的related_name为items),Django ORM不允许在创建主表记录时直接对反向关联集合赋值,必须先完成主表记录创建,再单独处理关联子表数据的写入。
你的现有代码还存在两个额外问题:
- 重复定义了两次
create_user字段,前一个用于写入当前登录用户的HiddenField会被后一个read_only的UserSerializer完全覆盖,无法自动注入当前操作人 - 序列化器未定义可写的
contragent外键字段,仅配置了只读的contragent_detail,无法接收请求中传递的往来单位参数
修正步骤
1. 调整序列化器字段配置
修正重复字段问题,补全可写外键字段:
class ConsignmentNoteSerializer(serializers.ModelSerializer): # 写入用:自动注入当前登录用户,映射到模型的creator字段 create_user = serializers.HiddenField(default=serializers.CurrentUserDefault(), source='creator') # 读用:返回关联的用户、往来单位详情 creator_info = UserSerializer(source='creator', read_only=True) contragent_detail = ContragentSerializer(source='contragent', read_only=True) # 可写外键:接收往来单位主键值 contragent = serializers.PrimaryKeyRelatedField(queryset=Contragent.objects.all()) items = ConsignmentItemSerializer(many=True) class Meta: model = ConsignmentNote fields = ['id', 'doc_type', 'date', 'number', 'contragent', 'contragent_detail', 'comment', 'create_user', 'creator_info', 'items', 'created']
2. 重写正确的create方法
按照「先创建主表、再批量创建子表关联数据」的逻辑实现:
def create(self, validated_data): # 先弹出嵌套的商品明细数据 items_data = validated_data.pop('items') # 创建销货单主记录 consignment_note = ConsignmentNote.objects.create(**validated_data) # 循环创建所有商品明细,绑定外键关联到刚创建的销货单 for item_data in items_data: # 注意:如果你的ConsignmentItem模型中关联ConsignmentNote的外键字段名不是consignment_note,请替换为实际字段名 ConsignmentItem.objects.create(consignment_note=consignment_note, **item_data) return consignment_note
3. 调整请求参数格式
对应序列化器的字段名调整请求JSON,创建场景不需要传自增id,去掉多余的尾逗号:
{ "doc_type": "capitalize", "date": "2022-06-04", "number": 98, "contragent": 4, "comment": "", "items": [ { "product": 10, "buy_price": "200.00", "sell_price": "500.00", "quantity": 5 } ] }
补充说明:如果后续需要实现更新接口,处理嵌套items数据时,对已经存在的item可以调用
consignment_note.items.set(目标明细列表)完成关联,也可以逐个做新增、修改、删除的判断处理。
内容的提问来源于stack exchange,提问作者sk1p
相关产品推荐
相关产品推荐

