如何在嵌套Serializer中通过ID提交数据并返回完整嵌套结构
问题描述
在餐厅订单系统中,尝试通过嵌套Serializer仅提交dish的id字段创建/更新订单,但GET请求返回时,dish仅显示id,期望返回dish的完整详细信息(id、name、price)。
提交的数据格式
{ "is_done": false, "dishes": [ {"dish":1} ] }
当前GET请求返回结果
{ "id": 14, "is_done": false, "dishes": [ { "dish": 1, "quantity": 1 } ] }
期望返回结构
{ "id": 14, "is_done": false, "dishes": [ { "dish": { "id": 1, "name": "Simple dish 1", "price": "200.00" }, "quantity": 1 } ] }
现有代码
OrderItemSerializer 与 OrderSerializer
class OrderItemSerializer(serializers.ModelSerializer): class Meta: model = OrderItem fields = ( 'dish', 'quantity', ) class OrderSerializer(serializers.ModelSerializer): dishes = OrderItemSerializer(many=True, read_only=False) class Meta: model = Order fields = ( 'id', 'is_done', 'dishes', # 'created_at', # 'updated_at', ) def create(self, validated_data): order_items = validated_data.pop('dishes') order = Order.objects.create(**validated_data) for dish in order_items: OrderItem.objects.create(order=order, **dish) return order def update(self, instance, validated_data): order_items = validated_data.pop('dishes') instance.is_done = validated_data.get('is_done', instance.is_done) instance.save() for dish in order_items: order_item = OrderItem.objects.get(order=instance) order_item.dish = dish['dish'] order_item.quantity = dish['quantity'] order_item.save() return instance
Models
class Order(models.Model): is_done = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = 'Заказ' verbose_name_plural = 'Заказы' class OrderItem(models.Model): order = models.ForeignKey(Order, related_name='dishes', on_delete=models.CASCADE) dish = models.ForeignKey(Dish, on_delete=models.CASCADE) quantity = models.IntegerField(default=1)
解决方案
要实现提交时仅传dish id,返回时展示dish完整信息,需要修改OrderItemSerializer,让dish字段在读取时返回嵌套的详细数据,写入时接受id。
步骤1:定义DishSerializer
首先确保你有Dish对应的Serializer(如果还没有,创建它):
class DishSerializer(serializers.ModelSerializer): class Meta: model = Dish fields = ('id', 'name', 'price')
步骤2:修改OrderItemSerializer
有两种常用方式实现需求:
方式一:拆分读写字段
通过拆分dish和dish_id字段,分别处理读取和写入:
class OrderItemSerializer(serializers.ModelSerializer): # 读取时返回dish的详细信息 dish = DishSerializer(read_only=True) # 写入时接受dish的id,映射到dish字段 dish_id = serializers.PrimaryKeyRelatedField( queryset=Dish.objects.all(), source='dish', write_only=True ) class Meta: model = OrderItem fields = ('dish', 'dish_id', 'quantity')
此时提交数据需要调整为:
{ "is_done": false, "dishes": [ {"dish_id":1} ] }
方式二:重写to_representation方法(保持提交格式不变)
如果想保持原提交格式(仍用{"dish":1}),可以重写to_representation方法,在返回时替换dish id为详细信息:
class OrderItemSerializer(serializers.ModelSerializer): class Meta: model = OrderItem fields = ('dish', 'quantity') def to_representation(self, instance): # 先获取默认的返回数据 data = super().to_representation(instance) # 将dish的id替换为完整的Dish序列化数据 data['dish'] = DishSerializer(instance.dish).data return data
这种方式不需要修改提交格式,原{"dish":1}的提交依然有效,同时GET请求会返回dish的完整信息。
额外优化:修复update方法的潜在问题
原update方法中,OrderItem.objects.get(order=instance)会获取该订单下的第一个OrderItem,若订单有多个菜品会导致错误。建议调整为批量重建菜品(根据业务需求选择):
def update(self, instance, validated_data): order_items_data = validated_data.pop('dishes') instance.is_done = validated_data.get('is_done', instance.is_done) instance.save() # 清空原订单菜品后重新创建(适合全量更新场景) instance.dishes.all().delete() for item_data in order_items_data: OrderItem.objects.create(order=instance, **item_data) return instance
若需增量更新(修改已有菜品数量),则需要根据dish id匹配对应的OrderItem进行更新。
内容的提问来源于stack exchange,提问作者Egor-oop
相关产品推荐
相关产品推荐

