【问题标题】:validated_data dropping some fields during POST requests on validation in .create(): nested serializer在 .create() 中验证的 POST 请求期间,validated_data 删除了一些字段:嵌套序列化程序
【发布时间】:2020-10-26 21:40:14
【问题描述】:

: purchased_products = validated_data.pop("products") KeyError: 'products'

我在产品和购买之间存在 M2M 关系。我想要实现的是在进行购买时,也填充 PurchasedProduct(通过模型)模型。但是每次我将数据发送到 API 并尝试从 valid_data 访问序列化程序中的 products 键时,都会引发 keyError 异常,但如果我返回 valid_data 以调试 product 键是回应。

djangorestframework==3.11.0 django==2.2.10

class Product(Model):
    name = CharField(max_length=20, unique=True)
    date_added = DateTimeField(default=now)

models.py

class Purchase(Model):
    manager = ForeignKey('users.User', on_delete=PROTECT, related_name='purchases')
    quantity = DecimalField(max_digits=6, decimal_places=2)
    products = ManyToManyField('branches.Product', through='PurchasedProduct',
                               through_fields=('purchase', 'product'))
    amount_fc = IntegerField(default=0)
    amount_usd = IntegerField(default=0)
    total_amount = IntegerField(default=0)
    date_purchased = DateTimeField(default=now)


class PurchasedProduct(Model):
    purchase = ForeignKey('Purchase', on_delete=CASCADE, related_name="to_products", blank=True)
    product = ForeignKey('branches.Product', on_delete=CASCADE, related_name='purchases')
    unit_price = DecimalField(max_digits=12, decimal_places=4, default=0.00)
    quantity = DecimalField(max_digits=5, decimal_places=2)
    amount_fc = DecimalField(max_digits=10, decimal_places=2)
    date_purchased = DateTimeField(default=now)

serializer.py

class PurchasedProductSerializer(ModelSerializer):

    class Meta:
        model = PurchasedProduct
        fields = [
            "id",
            "purchase",
            "product",
            "unit_price",
            "quantity",
            "amount_fc",
            "date_purchased"
        ]


class PurchaseSerializer(ModelSerializer):
    # https://github.com/encode/django-rest-framework/issues/5403
    products = PurchasedProductSerializer(source="to_products", many=True)

    class Meta:
        model = Purchase
        fields = [
            "id",
            "manager",
            "quantity",
            "amount_fc",
            "amount_usd",
            "total_amount",
            "products",
            "date_purchased"
        ]

    def create(self, validated_data):
        purchased_products = validated_data.pop("products")
        manager = validated_data.pop('manager')
        quantity = validated_data.pop('quantity')
        amount_fc = validated_data.pop('amount_fc')
        amount_usd = validated_data.pop('amount_usd')
        total_amount = validated_data.pop('total_amount')

        purchase = Purchase.objects.create(
            manager=manager,
            quantity=quantity,
            amount_fc=amount_fc,
            amount_usd=amount_usd,
            total_amount=total_amount
        )

        for purchased_product in purchased_products:
            product = Product.objects.get(pk=purchased_product.pop("product"))

            purchase.products.add(product, through_default=purchased_product)
        return purchase

view.py

class PurchasesListView(ListCreateAPIView):
    queryset = Purchase.objects.all()
    serializer_class = PurchaseSerializer
    permission_classes = [AllowAny]
    filterset_fields = ['date_purchased', 'manager']

数据

{
    "amount_fc": 13303340.0,
    "amount_usd": 1500,
    "manager": 2,
    "quantity": 100,
    "total_amount": 1230945,
    "products": [
        {
            "amount_fc": 1200334,
            "product": 8,
            "quantity": 120, 
            "unit_price": 10003.34
        },
        {
            "amount_fc": 1600334,
            "product": 6,
            "quantity": 100, 
            "unit_price": 16003.34
        }
    ]
}

错误:

purchased_products = validated_data.pop("products") KeyError: 'products'

但是当我将purchased_products = validated_data.pop("products") 更改为purchased_products = validated_data.pop("products", []) 时,它可以工作,但不能填充直通模型(PurchasedProduct)

我的尝试

DRF 示例 DRF doc exampe

drf 可写嵌套 drf-writable-nested 不支持直通模式的M2M

删除源 products = PurchasedProductSerializer(many=True) 添加了 write_only=True read_only=False。我也尝试抑制 Product 模型上的 uniqueValidator,但仍然无法正常工作。

【问题讨论】:

  • 为什么使用pop()?但即使你使用get(),如果validate_data("products") 不存在,你也会得到None
  • 完全正确。即使.get() 也不起作用。由于某些原因,当我发出 POST 请求时,它不包含在 valid_data 中,但 GET 可以正常工作

标签: python django django-rest-framework


【解决方案1】:

经过一番谷歌搜索和一些尝试和错误。我找到了解决这个问题的两种方法。

  1. 第一种是直接从self.context获取密钥。
purchased_products = self.context['request'].data['products']

而且效果很好。但我仍然不明白为什么在尝试从validated_data 获取products 时会抛出KeyError

  1. 第二个选项是使用drf-nested。 向团队大喊。????

该包为您的序列化程序提供了.create().update() 的实现以及更多,最重要的是它们支持M2Mthrough 模型的关系。 their implementation examples

【讨论】:

    猜你喜欢
    • 2017-12-07
    • 2019-02-19
    • 1970-01-01
    • 2018-10-06
    • 2017-11-12
    • 1970-01-01
    • 2016-11-21
    • 2018-07-06
    • 2020-09-29
    相关资源
    最近更新 更多