【问题标题】:Proper way to handle nested relationships处理嵌套关系的正确方法
【发布时间】:2016-07-12 21:48:51
【问题描述】:

我有一个案例,我好奇了很久很久。假设我有 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,而 PurchasedProductSerializer 又使用嵌套的 ProductSerializer。这很好,因为我获得了所有必要的信息,例如,使用 React 中的适当组件显示购买了哪些特定产品以及购物期间的数量。

然而,当我需要POST new PurchasedProduct 时,我遇到的问题是。我希望最方便的形式是:

{
    "quantity": 10,
    "purchase": 1,
    "product": 1
}

因为它携带所有必要的信息并且占用空间最小。但是我无法使用PurchasedProductSerializer 完成,因为它需要product 成为对象而不是id

所以我的问题是,这是一个好方法吗(对我来说似乎很自然),我应该为GETPOST 使用两个单独的序列化程序吗?我应该以不同的方式执行吗?您能否指出一些如何编写 API 和客户端应用程序的最佳实践/书籍?

【问题讨论】:

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


    【解决方案1】:

    几个月前我遇到了完全相同的问题,如果有人告诉我,我会非常高兴。我最终得到了您提出的将产品添加到购买中的确切解决方案。我同意您提出的POST 请求,这是最自然的方式,所需的足迹最少。

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

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-23
      • 1970-01-01
      • 2019-01-16
      相关资源
      最近更新 更多