web-dev-qa-db-fra.com

Django Rest Framework sérialiseurs imbriqués inscriptibles

J'écris un organisateur de recettes comme exemple de projet pour une classe. Je ne suis pas très expérimenté avec DRF à part utiliser des fonctionnalités très basiques. Voici l'objectif:

Créez une nouvelle recette avec les ingrédients associés. Créez les objets Ingrédient en même temps que la création de l'objet Recette.

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

Cela crée avec succès l'objet Recette ET les objets Ingrédients dans la base de données, mais n'associe pas la liste des ingrédients à la recette. Je suppose que c'est parce que lorsque j'exécute ingredients_data = validated_data.pop('ingredients'), le dictionnaire validated_data Obtient ses ingrédients supprimés, donc quand je crée une nouvelle recette en utilisant validated_data, Il n'y a pas d'ingrédients associés .

Cependant, je n'arrive pas à trouver un moyen de conserver les ingrédients associés à la recette.

36
bobbyz

J'ai compris que les relations ManyToMany ne peuvent pas être établies tant que tous les objets non créés n'ont pas été créés. (Voir les Django Docs page sur les relations plusieurs-à-plusieurs .))

Voici le code de travail:

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

MISE À JOUR:

Par demande de @StevePiercy, voici mon code update(). Cependant , je n'ai pas regardé cela depuis des années et je n'ai aucune idée que ce soit correct ou bon. Je ne travaille pas depuis Python ou Django depuis un certain temps maintenant, alors prenez ceci avec un grain de sel:

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
38
bobbyz

Ceci est un petit exemple avec cette question.

Modifiez cette partie du code par ceci.

    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

Et c'est la méthode pour éditer, provoquer une erreur lorsque vous souhaitez éditer.

    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

Voici un lien avec un exemple, ce code est similaire à un autre réponses, mais si vous voulez essayer le code, sans problème voici le repo. Bonne chance! sérialiseurs imbriqués DRF

9
fercreek