DRF中Serializer.data处理多对多关联时抛出AttributeError
问题原因分析
你遇到的AttributeError核心原因是:OrderSerializer的items字段默认关联了Order模型中的items(ManyToMany到MenuItem的集合),但你实际需要序列化的是关联的OrderItem实例。
当DRF执行serializer.data时,会尝试用OrderItemSerializer去序列化MenuItem实例,而OrderItemSerializer中的menu_item_id字段指定了source='menu_item',这会试图在MenuItem实例上查找menu_item属性——显然MenuItem实例没有这个属性,因此抛出错误。
解决方案
有两种简洁的修复方式,选其一即可:
方案一:修改序列化器的source指向
直接在OrderSerializer的items字段中指定source为Order关联的OrderItem集合(默认反向关联名为orderitem_set):
class OrderSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) customer = serializers.CharField(max_length=50) order_time = serializers.DateTimeField(read_only=True) # 指定source为Order的OrderItem反向关联集合 items = OrderItemSerializer(many=True, source='orderitem_set') def create(self, validated_data): # 同步修改create方法中取出的字段名 order_items_data = validated_data.pop('orderitem_set') order = Order.objects.create(**validated_data) for order_item_data in order_items_data: quantity = order_item_data.pop('quantity') menu_item = order_item_data.pop('menu_item') OrderItem.objects.create(order=order, quantity=quantity, menu_item=menu_item) return order
方案二:修改模型的反向关联名称(更优雅)
修改OrderItem模型的外键字段,添加related_name,让Order实例的items属性直接指向OrderItem集合,同时重命名Order模型的ManyToMany字段避免冲突:
1. 修改模型
class Order(models.Model): class OrderStatus(models.TextChoices): NEW = "new" READY = "ready" DELIVERED = "delivered" customer = models.CharField(max_length=50) order_time = models.DateTimeField(auto_now_add=True) # 重命名ManyToMany字段,避免和OrderItem的related_name冲突 menu_items = models.ManyToManyField(MenuItem, through='OrderItem') status = models.CharField(max_length=50, choices=OrderStatus.choices, default=OrderStatus.NEW) class OrderItem(models.Model): # 添加related_name='items',让Order.items直接关联该Order的所有OrderItem order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items') menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE) quantity = models.PositiveIntegerField()
2. 保持序列化器不变
此时OrderSerializer的items字段会自动关联Order的OrderItem集合,无需修改:
class OrderSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) customer = serializers.CharField(max_length=50) order_time = serializers.DateTimeField(read_only=True) items = OrderItemSerializer(many=True) def create(self, validated_data): order_items_data = validated_data.pop('items') order = Order.objects.create(**validated_data) for order_item_data in order_items_data: quantity = order_item_data.pop('quantity') menu_item = order_item_data.pop('menu_item') OrderItem.objects.create(order=order, quantity=quantity, menu_item=menu_item) return order
修复后效果
执行原请求后,serializer.data会正确返回包含menu_item_id的响应:
{ "id": 1, "customer": "John Doe", "order_time": "2024-05-20T12:34:56Z", "items": [ {"id": 1, "menu_item_id": 1, "quantity": 2}, {"id": 2, "menu_item_id": 2, "quantity": 1} ] }
内容的提问来源于stack exchange,提问作者user1584120
相关产品推荐
相关产品推荐

