处理嵌套关系的正确方法

时间:2016-07-12 21:48:52

标签: reactjs django-rest-framework vue.js

我有一个案例,我很长一段时间都很好奇。说我有3个型号:

class Product(models.Model):
    manufacturer = models.CharField(max_length=100)
    description = models.TextField(blank=True)

class PurchasedProduct(models.Model):
    product = models.ForeignKey(Product)
    purchase = models.ForeignKey('Purchase')
    quantity = models.PositiveIntegerField()

class Purchase(models.Model):
    customer = models.ForeignKey('customers.Customer')
    products = models.ManyToManyField(Product, through=PurchasedProduct)
    comment = models.CharField(max_length=200)

我有一个用一些JavaScript框架编写的API和客户端应用程序。所以现在我需要在他们之间进行沟通!我不确定如何处理DRF中的这种情况,当然我希望在访问/purchase/1/

时能得到类似的结果
{
    "id": 1,
    "customer": 1,
    "comment": "Foobar",
    "products": [
        {
            "id": 1,
            "product": {
                "id": 1,
                 ....
            },
            ....
        },
        ....
    ]
}

因此,我创建了正确的序列化程序,指定products字段应使用PurchasedProductSerializer,而ProductSerializer依次使用嵌套的POST。这很好,因为我得到所有必要的信息,比如显示购买的具体产品以及在购物期间使用适当的组件来说明React。

但是,当我需要PurchasedProduct{ "quantity": 10, "purchase": 1, "product": 1 } 时,问题就出现了。我希望最方便的形式是:

PurchasedProductSerializer

因为它包含所有必要的信息并且占地面积最小。但是,我无法使用product来完成,因为它需要id而不是GET

所以我的问题在这里,这是一个很好的方法(对我来说似乎很自然),我应该为POSTvar scale = ? var originX = ? var originY = ? var rotation = ? ctx.setTransform(scale,0,0,scale,originX,originY); ctx.rotate(rotation); 使用两个单独的序列化器吗?我的表现应该不同吗?您能否指点一些最佳实践/书籍如何编写API和客户端应用程序?

1 个答案:

答案 0 :(得分:1)

几个月前我遇到了完全相同的问题,如果有人告诉我的话,我会非常高兴。我最终得到了您建议将产品添加到购买中的确切解决方案。我同意你提出的POST请求它是最简单的占用空间最自然的方式。

为了正确处理POST请求的数据,我最终使用了两个独立的Serializer,就像你描述的那样。如果您正在使用DRF视图集,则在GETPOST上选择正确序列化程序的一种方法是覆盖here所述的get_serializer_class方法。

POST请求的反序列化器可能如下所示:

class PurchasedProductDeserializer(serializers.ModelSerializer):
  product = serializers.PrimaryKeyRelatedField(queryset=Product.objects.all())
  purchase = serializers.PrimaryKeyRelatedField(queryset=Purchase.objects.all())

  class Meta:
    model = PurchasedProduct
    fields = ('id', 'product', 'purchase', 'quantity')
    write_only_fields = ('product', 'purchase', 'quantity')

然后可以将该反序列化器用于输入验证,最后将产品添加到购买中(或增加其数量)。

例如,在您的视图集中:

def create(self, request, *args, **kwargs):
  # ...
  # init your serializer here
  serializer = self.get_serializer(data=request.data)
  if serializer.is_valid(raise_exception=True):
    # now check if the same item is already in the cart
    try:
      # try to find the product in the list of purchased products
      purchased_product = serializer.validated_data['purchase'].purchasedproduct_set.get(product=serializer.validated_data['product'])
      # if so, simply increase its quantity, else add the product as a new item to the cart (see except case)
      purchased_product.quantity += serializer.validated_data['quantity']
      purchased_product.save()
      # update the serializer so it knows the id of the existing instance
      serializer.instance = purchased_product
    except PurchasedProduct.DoesNotExist:
      # product is not yet part of the purchase cart, add it now
      self.perform_create(serializer)
  # ...
  # do other stuff here

至于最佳做法,互联网上有大量文档可供使用,但如果您正在寻找书籍,您可能需要查看一些已发布的文章here。当你对REST感到厌倦时,你甚至可能想看看GraphQL