Django Rest Framework - 创建和更新父子关系

Wou*_*ens 6 python django django-serializer django-rest-framework

我正在尝试使用 Django Rest Framework 序列化程序为我的前端创建 API,以在商店中创建/更新和删除文章。这些文章可以有多个价格(取决于时间)。因此,商品(一)与价格(多)之间存在一对多关系。我已在中定义了这一点models.py

\n\n
class Article(models.Model):\n    article_id = models.AutoField(primary_key=True, verbose_name="Article ID")\n    article_name = models.CharField(max_length=250, verbose_name="Name")\n    article_order = models.IntegerField(verbose_name="Order")\n\nclass Price(models.Model):\n    price_id = models.AutoField(primary_key=True, verbose_name="Price ID")\n    article_id = models.ForeignKey(Article, on_delete=models.CASCADE, verbose_name="Article ID", related_name="Prices")\n    price_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="Price")\n
Run Code Online (Sandbox Code Playgroud)\n\n

我的serializers.py 文件如下所示:

\n\n
from rest_framework import serializers\nfrom .models import *\n\nclass PriceSerializer(serializers.ModelSerializer):\n\n    class Meta:\n        model = Price\n        fields = (\'price_price\',)\n\nclass ArticleSerializer(serializers.ModelSerializer):\n\n    Prices = PriceSerializer(many=True)\n\n    def create(self, validated_data):\n        prices_data = validated_data.pop(\'Prices\')\n        article = Article.objects.create(**validated_data)\n        for price_data in prices_data:\n            Price.objects.create(article_id=article, **price_data)\n        return article\n\n    def update(self, instance, validated_data):\n        prices_data = validated_data.pop(\'Prices\')\n        Article.objects.filter(article_id=instance.article_id).update(**validated_data)\n        for price_data in prices_data:\n            Price.objects.get_or_create(article_id=instance, **price_data)\n        return instance\n\n    class Meta:\n        model = Article\n        fields = \'__all__\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

这非常有效,我可以使用此数据创建一个新的文章价格测量:(article_order稍后将用于订购列表)

\n\n
{"Prices":[{"price_price":"1"}],"article_name":"Article A","article_order":"1"}\n
Run Code Online (Sandbox Code Playgroud)\n\n

到目前为止,一切都按预期进行。但当我尝试更新价格时,该Price.objects.get_or_create()声明无法识别现有价格。例如:当第一个价格为 \xe2\x82\xac10,然后为 \xe2\x82\xac20,然后再次为 \xe2\x82\xac10 时,将不会插入最后一个价格,因为该语句不会将其识别get_or_create为新实例模型的price。这可能是有道理的,因为它PriceSerializer没有序列化price_id. 但是当我将序列化器更改为:

\n\n
class PriceSerializer(serializers.ModelSerializer):\n\n    class Meta:\n        model = Price\n        fields = \'__all__\n
Run Code Online (Sandbox Code Playgroud)\n\n

,我无法再创建实例,因为ArticleSerializer需要article_id实例Price(但尚不存在)。

\n\n

有谁知道如何解决这个问题?DRF 文档仅包含此类嵌套序列化器的创建语句。

\n\n

https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers

\n

Ken*_*ars 2

你没有get_or_create正确使用该方法。它接受kwargs用于查找对象的字段,如果不存在,则创建它们,并添加来自 的其他字段值defaults

这意味着,如果您将新值传递给 update as kwargs,它将找不到此类对象,因为这些字段当前具有不同的值。相反,您应该传入不更改的字段kwargs,并将其余字段传入defaults.

这就是你应该做的

def update(self, instance, validated_data):
    prices_data = validated_data.pop('Prices')
    Article.objects.filter(article_id=instance.article_id).update(**validated_data)
    for price_data in prices_data:
        Price.objects.get_or_create(article_id=instance, defaults=price_data)
    return instance
Run Code Online (Sandbox Code Playgroud)

但请注意,您可能会收到MutipleObjectsReturned错误消息,因为根据您的数据库架构,一篇文章可能有多个价格。如果不希望每篇文章有多个价格,您可能需要将 更改article_id为 aOneToOneField以防止出现这种情况。

get_or_create您可以在Django 文档中阅读更多相关内容

附带说明一下,在 OOP 架构中id已经Article暗示了文章 id,因此article在字段名称前面添加确实是一种多余且糟糕的做法。再说一遍,您可能想使用 Django 自动生成的 id 字段而不是重新定义它,除非您想以某种方式自定义它,而这在您的代码中目前并不明显