Интересно, как обработать 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"
}
}
]
}