0

我正在尝试使用我的 api 来创建和更新捆绑包中的产品。我这样做了:

模型.py

class Business(models.Model):
    name = models.CharField(max_length=155)

class Product(models.Model):
    business = models.ForeignKey(
        Business,
        on_delete=models.CASCADE,
        blank=True,
        null=True,
    )
    name = models.CharField(max_length=200)
    description = models.TextField(null=True, blank=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = "Product"



class Bundle(models.Model):
    business = models.ForeignKey(
        Business,
        on_delete=models.CASCADE,
        blank=True,
        null=True,
    )
    name = models.CharField(max_length=100)
    description = models.TextField(null=True, blank=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    products = models.ManyToManyField(Product, related_name="bundles",blank=True, null=True, through="BundleProduct")

    class Meta:
        verbose_name = "Bundle"


    def __str__(self):
        return self.name

class BundleProduct(models.Model):

    bundle = models.ForeignKey(Bundle, on_delete=models.CASCADE, related_name="bundleproducts")
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="bundleproducts")
    number = models.IntegerField(default=1)

    class Meta:
        verbose_name = "Bundle of Product"


    def __str__(self):
        return str(self.product.name) + " do " + self.bundle.name

    def get_absolute_url(self):
        return reverse("BundleProduct_detail", kwargs={"pk": self.pk})

这是我的serializers.py:

class ProductSerializer(serializers.ModelSerializer):

    class Meta:
        model = Product
        fields = "__all__"        

class BundleProductSerializer(serializers.ModelSerializer):

    class Meta:
        model = BundleProduct
        fields = "__all__"


class BundleSerializer(serializers.ModelSerializer):

    class Meta:
        model = Bundle
        fields = "__all__"

我的视图集.py

class ProductViewSet(viewsets.ModelViewSet):

    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    model = Product


class BundleProductViewSet(viewsets.ModelViewSet):

    queryset = BundleProduct.objects.all()
    serializer_class = BundleProductSerializer
    model = BundleProduct


class BundleViewSet(viewsets.ModelViewSet):

    queryset = Bundle.objects.all()
    serializer_class = BundleSerializer
    model = Bundle

当我尝试在 bundleproducts 中发布一些产品时,我收到“类型不正确。预期的 pk 值,收到的列表。”

阅读此错误后,我发现了一些与 PrimaryKeyRelatedField 和 SlugRelatedField 相关的问题。我知道我需要覆盖,但我不知道该怎么做。

这是一个如何发布的示例:

{
    "number": 1,
    "bundle": 2,
    "product": 
         [
            1,
            2
         ]
}

在观看了 Neil 评论的视频后,我创建了以下方法:

class BundleSerializer(
    serializers.ModelSerializer
):
    products = ProductSerializer(many=True)

    def create(self, validated_data):
        products = validated_data.pop('products')
        bundle = BundleProduct.objects.create(**validated_data)
        for product in products:
            BundleProduct.objects.create(**product, bundle=bundle)
        return Bundle


    class Meta:
        model = Bundle
        fields = "__all__"

但不起作用。我收到此错误:“TypeError at /api/v1/bundle/

'name' 是此函数的无效关键字参数"

4

3 回答 3

0

这里的问题是您将列表发布到BundleProduct的产品字段,但它是一个ForeignKey. 要加入Bundlea Product,只需POST

{
  "bundle": 2,
  "product" 1,
  "number": 1
}

你可以重复这个:

{
  "bundle": 2,
  "product" 4,
  "number": 1
}

将另一个产品 4 添加到同一个捆绑包中,依此类推。只要确保你一个一个地做它们,而不是像你之前做的那样在一个列表中。

于 2019-02-25T20:04:17.917 回答
0

如果您通过 BundleSerializer 发布帖子,则需要传递带有 ProductSerializer 数据列表的产品,而不仅仅是 id,因为 BundleSerializer 中的产品正在接受 productsSerializer 数据。您收到类型错误“名称”是此函数的无效关键字参数”,因为您的验证数据包含名称和 BundleProduct 对象没有名称字段。并且您正在使用验证数据创建 BundleProduct 对象。

创建捆绑对象并将捆绑对象的 id 传递给 BundleProduct 对象。

  • 如果您不想创建产品而只传递现有的产品 ID,则需要制作 ListField

  • 您需要覆盖get_fields并检查请求

  • 覆盖to_representation以始终返回 ProdutSerializer 数据列表
  • 覆盖POST请求的创建
  • 覆盖PUTPATCH请求的更新

以下是POST请求的解决方案

对于PATCH AND PUT请求,您需要覆盖 ModelSerializer 的更新方法并相应地处理产品。


class BundleSerializer(serializers.ModelSerializer):

    def create(self, validated_data):
        products = validated_data.pop('products')
        bundle = Bundle.objects.create(**validated_data)
        for product_id in products:
            product = get_object_or_404(Product, pk=product_id)
            BundleProduct.objects.create(product=product, bundle=bundle)
        return bundle

    class Meta:
        model = Bundle
        fields = "__all__"

    def to_representation(self, instance):
        repr = super().to_representation(instance)
        repr['products'] = ProductSerializer(instance.products.all(), many=True).data
        return repr

    def get_fields(self):
        fields = super().get_fields()
        if self.context['request'].method in ['POST', "PATCH","PUT"]:
            fields['products'] = serializers.ListField(
                write_only=True,
                child=serializers.IntegerField()
            )
        return fields

将 POST 数据示例到 BundleSerializer

{
    "products":[1,2],
    "name":"Offer One",
    "description":"description",
    "price":1212,
    "business":1

}
于 2019-02-26T11:41:11.673 回答
0

根据我的经验,如果您想在一个请求中更新模型和相关模型,使用 DRF,最简单的方法是覆盖序列化程序的“创建”方法。这里有一个很好的视频,我用作参考:https ://www.youtube.com/watch?v=EyMFf9O6E60

于 2019-02-25T15:13:38.887 回答