You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在嵌套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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 10:06:24