【问题标题】:Django Rest Framework writable nested serializersDjango Rest Framework 可写嵌套序列化器
【发布时间】:2015-03-20 14:44:37
【问题描述】:

我正在编写一个食谱组织者作为课程的示例项目。除了使用一些非常基本的功能外,我对 DRF 不是很有经验。这是目标:

创建一个包含相关成分的新配方。在创建配方对象的同时创建成分对象。

models.py:

class Ingredient(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class Recipe(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField(blank=True, null=True, help_text="This is a quick description of your recipe")
    directions = models.TextField(help_text="How to make the recipe")
    ingredients = models.ManyToManyField(Ingredient)

    def __str__(self):
        return self.name


serializers.py

class IngredientSerializer(serializers.ModelSerializer):

    class Meta:
        model = Ingredient


class RecipeSerializer(serializers.ModelSerializer):
    ingredients = IngredientSerializer(many=True)

    class Meta:
        model = Recipe

    def create(self, validated_data):
        ingredients_data = validated_data.pop('ingredients')
        recipe = Recipe.objects.create(**validated_data)
        for ingredient_data in ingredients_data:
            Ingredient.objects.create(**ingredient_data)
        return recipe

这成功地在数据库中创建了食谱对象和成分对象,但不会将成分列表与食谱相关联。我认为这是因为当我运行 ingredients_data = validated_data.pop('ingredients') 时,validated_data 字典会删除其成分,所以当我使用 validated_data 创建新配方时,没有相关的成分。

但是我似乎无法找到一种方法来保持与食谱相关的成分。

【问题讨论】:

    标签: python django serialization django-rest-framework


    【解决方案1】:

    我发现,在创建所有未创建的对象之前,无法建立多对多关系。 (参见 Django 文档page on many-to-many relationships。)

    这是工作代码:

    serializers.py

    class RecipeSerializer(serializers.ModelSerializer):
        ingredients = IngredientSerializer(many=True)
    
        class Meta:
            model = Recipe
    
        def create(self, validated_data):
            ingredients_data = validated_data.pop('ingredients')
            recipe = Recipe.objects.create(**validated_data)
    
            for ingredient in ingredients_data:
                ingredient, created = Ingredient.objects.get_or_create(name=ingredient['name'])
                recipe.ingredients.add(ingredient)
            return recipe
    

    更新:

    根据@StevePiercy 的要求,下面是我的update() 代码。 然而,我已经很多年没有看过这个了,也不知道它是正确的还是好的。我已经有一段时间没有在 Python 或 Django 中工作了,所以对此持保留态度:

    def update(self, instance, validated_data):
        ingredients_data = validated_data.pop('ingredients')
    
        instance.name = validated_data.get('name', instance.name)
        instance.description = validated_data.get('description', instance.description)
        instance.directions = validated_data.get('directions', instance.directions)
        instance.photo = validated_data.get('photo', instance.photo)
    
        ingredients_list = []
    
        for ingredient in ingredients_data:
            ingredient, created = Ingredient.objects.get_or_create(name=ingredient["name"])
            ingredients_list.append(ingredient)
    
        instance.ingredients = ingredients_list
        instance.save()
        return instance
    

    【讨论】:

    • 你用成分列表做什么?
    • @iankit 哎呀,我意识到我已经从我的 update() 方法中粘贴了一个 sn-p。我已经修复它以反映正确的代码。谢谢!
    • 我注意到当我尝试做类似的事情时,我得到“HTML 输入当前不支持列表”。在可浏览的 API 上。有关如何解决此问题的任何提示?
    • @bobbyz 请您也分享一下您的update() 方法好吗?
    【解决方案2】:

    下面是这个问题的一个有用的例子。

    像这样更改那部分代码:

    def create(self, validated_data):
        ingredients_data = validated_data.pop('ingredients')
        recipe = Recipe.objects.create(**validated_data)
    
        for ingredient in ingredients_data:
            ingredient, created = Ingredient.objects.get_or_create(name=ingredient['name'])
            recipe.ingredients.add(ingredient)
        return recipe
    

    这是编辑的方法,要编辑时会出错。

    def update(self, instance, validated_data):
        ingredients_data = validated_data.pop('ingredients')
        instance.name = validated_data['name']
        instance.description = validated_data['description']
        instance.directions = validated_data['directions']
    
        for ingredient in ingredients_data:
            ingredient, created = Ingredient.objects.get_or_create(name=ingredient['name'])
            recipe.ingredients.add(ingredient)
        return instance
    

    这是一个示例链接。此代码类似于另一个答案,但如果您想尝试代码没有任何问题,您可以使用这个 repo。祝你好运! DRF Nested serializers

    【讨论】:

    • 当代码看起来与接受的答案相同时,这是如何被否决的?
    • 我建议对健壮性进行这个小的更改:ingredient, created = ingredient.objects.get_or_create(**ingredients_data) 这样,字典作为 **kwargs 传递给构造函数,从而抽象了实际的类结构。否则,这个答案对我帮助很大!谢谢!
    猜你喜欢
    • 1970-01-01
    • 2023-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-23
    • 2017-07-28
    相关资源
    最近更新 更多