Django REST Framework多对多through中间模型可写序列化实现问题
错误根因
核心问题出在两个地方:
Order模型中的items字段是多对多关联Item的字段,直接调用order.items返回的是Item实例的查询集,但你给OrderSerializer的items字段配了OrderItemSerializer,序列化时会把Item实例传入OrderItemSerializer,后者要读取item属性,Item类自然不存在这个属性,直接触发报错。OrderItemSerializer中item字段用了嵌套的ItemSerializer,默认不支持直接传入Item主键创建关联,和你提交的请求格式不匹配。
正确实现方案
步骤1:修改OrderItemSerializer,适配读写两种场景
class OrderItemSerializer(serializers.ModelSerializer): # 读场景:返回Item的完整嵌套信息 item = ItemSerializer(read_only=True) # 写场景:接收Item主键,自动关联对应实例 item_id = serializers.PrimaryKeyRelatedField( queryset=Item.objects.all(), write_only=True, source='item' ) class Meta: model = OrderItem fields = ['item', 'item_id', 'quantity', 'price']
步骤2:修改OrderSerializer的关联字段,指向中间表反向查询集
Order到OrderItem的反向关联默认是orderitem_set,我们直接复用items作为输出字段名即可:
class OrderSerializer(serializers.ModelSerializer): # 指定source为中间表反向查询集,输出字段名仍为items items = OrderItemSerializer(many=True, source='orderitem_set') class Meta: model = Order fields = ['id','number', 'total', 'credits_issued', 'paid', 'expected_delivery', 'payment_type', 'date', 'status', 'note', 'ordered_by', 'location', 'vendor', 'items'] extra_kwargs = { 'ordered_by': {'required': True, 'read_only': False}, 'location': {'required': True, 'read_only': False}, 'vendor': {'required': True, 'read_only': False}, 'items': { 'required': True, 'read_only': False } } def create(self, validated_data): # 因为指定了source='orderitem_set',validated_data里的键名是orderitem_set items = validated_data.pop('orderitem_set') order = Order.objects.create(**validated_data) for item_data in items: OrderItem.objects.create(order=order, **item_data) # 可选:自动计算订单总金额 order.total = sum([i.quantity * i.price for i in order.orderitem_set.all()]) order.save(update_fields=['total']) return order def validate_items(self, value): if len(value) < 1: raise serializers.ValidationError("Order should have at least 1 item") return value def to_internal_value(self, data): if 'items' in data: if not isinstance(data.get('items', []), list): raise serializers.ValidationError({ 'items': ['Expected a list of items but got type {input_type}'.format(input_type=type(data.get('items')).__name__)] }) return super().to_internal_value(data)
步骤3:请求格式无需修改
你原来的请求示例完全兼容修改后的代码,提交后会自动生成Order记录和对应的OrderItem中间表记录,返回结果会自动带上每个商品的完整信息。
内容的提问来源于stack exchange,提问作者achchu93
相关产品推荐
相关产品推荐

