首页 > 解决方案 > Django Rest Framework - 创建和更新父子关系

问题描述

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

class Article(models.Model):
    article_id = models.AutoField(primary_key=True, verbose_name="Article ID")
    article_name = models.CharField(max_length=250, verbose_name="Name")
    article_order = models.IntegerField(verbose_name="Order")

class Price(models.Model):
    price_id = models.AutoField(primary_key=True, verbose_name="Price ID")
    article_id = models.ForeignKey(Article, on_delete=models.CASCADE, verbose_name="Article ID", related_name="Prices")
    price_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="Price")

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

from rest_framework import serializers
from .models import *

class PriceSerializer(serializers.ModelSerializer):

    class Meta:
        model = Price
        fields = ('price_price',)

class ArticleSerializer(serializers.ModelSerializer):

    Prices = PriceSerializer(many=True)

    def create(self, validated_data):
        prices_data = validated_data.pop('Prices')
        article = Article.objects.create(**validated_data)
        for price_data in prices_data:
            Price.objects.create(article_id=article, **price_data)
        return article

    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, **price_data)
        return instance

    class Meta:
        model = Article
        fields = '__all__'

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

{"Prices":[{"price_price":"1"}],"article_name":"Article A","article_order":"1"}

到目前为止,一切都按预期工作。但是当我尝试更新价格时,该Price.objects.get_or_create()声明不承认现有价格。例如:当第一个价格是 10 欧元,然后是 20 欧元,然后又是 10 欧元时,将不会插入最后一个价格,因为get_or_create语句不会将其识别为price模型的新实例。这可能是有道理的,因为PriceSerializer不会序列化price_id. 但是当我将序列化程序更改为:

class PriceSerializer(serializers.ModelSerializer):

    class Meta:
        model = Price
        fields = '__all__

,我不能再创建实例,因为实例需要ArticleSerializer一个(但还不存在)。article_idPrice

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

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

标签: pythondjangodjango-rest-frameworkdjango-serializer

解决方案


您没有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

但是请注意,您可能会收到MutipleObjectsReturned错误消息,因为一篇文章可以根据您的数据库架构有多个价格。如果不打算为每篇文章设置多个价格,您可能需要将其更改article_id为 a以防止这种情况发生。OneToOneField

您可以get_or_createDjango 文档中了解更多信息

附带说明一下,在 OOP 架构idArticle已经暗示了文章 ID,因此article在您的字段名称之前添加是一种非常多余和不好的做法。同样,您可能希望使用 Django 自动生成的 id 字段而不是重新定义它,除非您想以某种方式自定义它,这在您的代码中目前并不明显


推荐阅读