【问题标题】:Django double nested serializerDjango 双嵌套序列化器
【发布时间】:2021-06-24 14:06:46
【问题描述】:

我正在编写一个 api 应用程序,其中 Order 是一个实体,它提供有关客户想要购买的信息的信息,一个 Order 可以包含具有不同数量和不同价格的不同产品。在 Order 中包含此信息的实体称为 Order Detail。

我在使用双嵌套序列化程序时遇到了一些问题。

发布请求后:

{
    "external_id": "PR-123-321-123",
    "details": [{
        "product": {"id": 4},
        "amount": 10,
        "price": "12.00"
    }, ... ]
}

我想得到这个回复:

{
    "id": 1,
    "status": "new",
    "created_at": "2021-01-01T00:00:00",
    "external_id": "PR-123-321-123",
    "details": [{
        "id": 1,
        "product": {"id": 4, "name": "Dropbox"},
        "amount": 10,
        "price": "12.00"
    }, ... ]
}

但现在我遇到了一些错误:“无法分配“OrderedDict()”:“OrderDetail.product”必须是“Product”实例。”

怎么了?

型号:

from django.db import models


class Order(models.Model):
    STATUS_CHOICES = (
        ('new', 'new'),
        ('accepted', 'accepted'),
        ('failed', 'failed')
    )
    status = models.CharField(max_length=12, choices=STATUS_CHOICES, default='new')
    created_at = models.DateTimeField(auto_now_add=True)
    external_id = models.CharField(max_length=128, unique=True)


class Product(models.Model):
    name = models.CharField(max_length=64)

    def __str__(self):
        return self.name


class OrderDetail(models.Model):
    order = models.ForeignKey(Order, related_name='details', on_delete=models.CASCADE)
    amount = models.IntegerField()
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    price = models.DecimalField(max_digits=8, decimal_places=2)

    def __str__(self):
        return f'{self.order} detail'

序列化器:

from rest_framework import serializers
from rest_framework.serializers import ModelSerializer

from order.models import Order, Product, OrderDetail


class ProductSerializer(ModelSerializer):
    class Meta:
        model = Product
        fields = '__all__'
        read_only_fields = ('name', )


class OrderDetailSerializer(ModelSerializer):
    product = ProductSerializer()

    class Meta:
        model = OrderDetail
        fields = (
            'id',
            'product',
            'amount',
            'price'
        )


class OrderSerializer(ModelSerializer):
    details = OrderDetailSerializer(many=True)
    status = serializers.CharField(source='get_status_display', read_only=True)

    class Meta:
        model = Order
        fields = (
            'id',
            'status',
            'created_at',
            'external_id',
            'details'
        )

    def create(self, validated_data):
        details_data = validated_data.pop('details')
        order = Order.objects.create(**validated_data)
        for detail_data in details_data:
            OrderDetail.objects.create(**detail_data, order=order)
        return order

【问题讨论】:

    标签: django django-rest-framework django-serializer


    【解决方案1】:
        def create(self, *args, **kwargs):
            order = Order.objects.create(external_id=self.context.get('request').data.get('external_id'))
            OrderDetail.objects.bulk_create(
                [OrderDetail(**order_detail, order=order, product=Product.objects.get(**order_detail.pop('product')))
                 for order_detail in self.context.get('request').data.get('details')]
            )
            return order
    

    【讨论】:

      【解决方案2】:

      修改OrderSerializer的create方法,在OrderDetail对象创建之前先获取Product实例。

      class OrderSerializer(ModelSerializer):
          details = OrderDetailSerializer(many=True)
          status = serializers.CharField(source='get_status_display', read_only=True)
      
          class Meta:
              model = Order
              fields = (
                  'id',
                  'status',
                  'created_at',
                  'external_id',
                  'details'
              )
      
          def create(self, validated_data):
              details_data = validated_data.pop('details')
              order = Order.objects.create(**validated_data)
              for detail_data in details_data:
                  try:
                      product = Product.objects.get(**detail_data.pop('product'))
                      OrderDetail.objects.create(**detail_data, order=order, product=product)
                  except (Product.DoesNotExist, Product.MultipleObjectsReturned):
                      raise serializers.ValidationError('Your custom error message...')
              return order
      

      【讨论】:

      • 谢谢,但我又收到一个错误:“MultipleObjectsReturned at /api/v1/orders/get() 返回了多个产品 -- 它返回了 2 个!”
      • 我得到了除了错误,因为我还有一个产品。我想,也许问题是该产品已在订单中使用,但我创建了一个新产品并在发布请求中指出它的 ID,如上面问题正文中所述。这是我传递产品 ID 的请求正文:``` { "external_id": "PR-123-321-123", "details": [{ "product": {"id": 4}, "amount ": 10, "价格": "12.00" }] } ```
      【解决方案3】:
      class OrderSerializer(serializers.ModelSerializer):
          order = OrderDetailSerializer(many=True, source='order_set', required=False)
      
          class Meta:
              model = Order
              fields = (
                  'id',
                  'status',
                  'created_at',
                  'external_id',
                  'details'
              )
       
          def create(self, validated_data):
              if 'order_set' in validated_data:
                  order = validated_data.pop('order_set')
              else:
                  order = []    
              instance = super().create(validated_data)
      
              for cd in order:
                 instance.order_set.create(**cd)
              return instance
      
          
             
      

      【讨论】:

      • 谢谢,但我有自己的解决方案。
      猜你喜欢
      • 2017-07-12
      • 2020-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-03
      • 2017-07-28
      • 2018-06-23
      相关资源
      最近更新 更多