Переопределить функцию создания в сериализаторе Django - PullRequest
0 голосов
/ 30 октября 2018

Интересно, как обработать POST-запрос, чтобы правильно сохранить входящие данные, имея такие модели:

class Recipe(models.Model):
    author = models.ForeignKey('auth.user', related_name='recipes', on_delete=models.CASCADE)
    image = models.TextField(default='None')
    name = models.CharField(max_length=100)
    description = models.TextField(default='No description')
    votes = models.IntegerField(default=0)

    def __str__(self):
        return self.name

class Ingredient(models.Model):
    image = models.TextField(default='None')
    name = models.CharField(max_length=100)

    description = models.TextField(default='No description')
    price = models.DecimalField(max_digits=8, decimal_places=3)
    unit_price = models.DecimalField(max_digits=8, decimal_places=3)
    unit_quantity = models.CharField(max_length=20)

    def __str__(self):
        return self.name

Я хотел избежать дублирования объектов Ингредиента, поэтому для обеспечения количества определенного Ингредиента в Рецепте я создал модель RecipesIngredient, которая связывает Ингредиент с Рецептом, но также содержит количество этого Ингредиента:

class RecipesIngredient(models.Model):
        recipe = models.ForeignKey(Recipe, related_name='ingredients', on_delete=models.CASCADE)
        ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE)
        quantity = models.CharField(max_length=100)

        def __str__(self):
            return self.quantity

Я также подготовил сериализаторы для этих моделей:

class IngredientSerializer(HyperlinkedModelSerializer):
    class Meta:
        model = Ingredient
        fields = (
            'url',
            'image',
            'name',
            'description',
            'price',
            'unit_price',
            'unit_quantity'
        )

class RecipesIngredientSerializer(HyperlinkedModelSerializer):
    ingredient_name = ReadOnlyField(source='ingredient.name')
    ingredient_price = ReadOnlyField(source='ingredient.price')
    ingredient_unit_price = ReadOnlyField(source='ingredient.unit_price')
    ingredient_unit_quantity = ReadOnlyField(source='ingredient.unit_quantity')

    class Meta:
        model = RecipesIngredient
        fields = (
            'url',
            'ingredient_name',
            'quantity',
            'ingredient_price',
            'ingredient_unit_price',
            'ingredient_unit_quantity'
        )

class RecipeListSerializer(HyperlinkedModelSerializer):
    author = ReadOnlyField(source='author.username')
    author_url = ReadOnlyField(source='author.url')

    class Meta:
        model = Recipe
        fields = (
            'url',
            'author',
            'author_url',
            'image',
            'name',
            'description',
            'votes'
        )


class RecipeDetailSerializer(HyperlinkedModelSerializer):
    author = ReadOnlyField(source='author.username')
    author_url = ReadOnlyField(source='author.url')
    ingredients = RecipesIngredientSerializer(many=True)

    class Meta:
        model = Recipe
        fields = (
            'url',
            'author',
            'author_url',
            'image',
            'name',
            'description',
            'ingredients',
            'votes'
        )

Но в этом случае мне нужно сначала создать экземпляр Recipe и сохранить его в БД, а затем сделать то же самое с Ingredient, чтобы иметь возможность «связать» их в RecipesIngredient. Возможно ли обработать этот случай только с одним запросом POST для просмотра ниже?

#
# path('recipes/', views.RecipeList.as_view(), name='recipe-list')
#
class RecipeList(generics.ListCreateAPIView):
    queryset = Recipe.objects.all()
    serializer_class = RecipeListSerializer

    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

@ EDIT Я забыл об этой теме, но проблема решена. Я подготовил еще один сериализатор только для целей CREATE и переопределил функцию 'create' этого сериализатора .:

class RecipeCreateSerializer(HyperlinkedModelSerializer):
    #author = ReadOnlyField(source='author.username')
    #author_url = ReadOnlyField(source='author.url')
    recipes_ingredients = RecipesIngredientCreateSerializer(many=True)

    def create(self, validated_data):
        recipes_ingredients = validated_data.pop('recipes_ingredients')
        recipe_instance = super().create(validated_data)

        for recipe_ingredient in recipes_ingredients:

            ingredient_data = recipe_ingredient.pop('ingredient')
            ingredient_instance = Ingredient(
                image=ingredient_data['image'],
                name=ingredient_data['name'],
                description=ingredient_data['description'],
                price=ingredient_data['price'],
                unit_price=ingredient_data['unit_price'],
                unit_quantity=ingredient_data['unit_quantity'],
            )
            ingredient_instance.save()


            recipes_ingredient_instance = RecipesIngredient(
                recipe=recipe_instance,
                ingredient=ingredient_instance,
                quantity=recipe_ingredient['quantity']
            )
            recipes_ingredient_instance.save()
        return recipe_instance

    class Meta:
        model = Recipe
        fields = (
            'url',
            'image',
            'name',
            'description',
            'votes',
            'recipes_ingredients',
        )

Также JSON-файл выглядит немного по-другому, но все работает просто отлично:

{
    "image": "image-url",
    "name": "recipes-name",
    "description": "recipes-description",
    "votes": 0,
    "recipes_ingredients": [
        {
            "quantity": "ingredients-quantity",
            "ingredient": {
                "image": "image-url",
                "name": ingredient-name",
                "description": "ingredient-description",
                "price": 3.6,
                "unit_price": 0.36,
                "unit_quantity": "100ML"
            }
        },
        {
            "quantity": "ingredients-quantity",
            "ingredient": {
                "image": "image-url",
                "name": ingredient-name",
                "description": "ingredient-description",
                "price": 0.3,
                "unit_price": 0.3,
                "unit_quantity": "EACH"
            }
        },
        {
            "quantity": "ingredients-quantity",
            "ingredient": {
                "image": "image-url",
                "name": ingredient-name",
                "description": "ingredient-description",
                "price": 2.0,
                "unit_price": 0.8,
                "unit_quantity": "KG"
            }
        }
    ]
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...