There are two approaches to have this outcome [2] having another model:
- using
WritableNestedModelSerializer.
- Overwriting the
create() method of your serializer.
1st example, using WritableNestedModelSerializer:
# models.py --------------------------
class Ingredient(models.Model):
# model that will be related to Meal.
name = models.CharField(max_lenght=128)
amount = models.IntergerField()
def __str__(self):
return str(self.name)
class Meal(models.Model):
# meal model related to Ingredient.
name = models.TextField()
recipe = models.TextField()
ingredients = models.ForeigKey(Ingredient)
def __str__(self):
return str(self.name)
# serializers.py ----------------------
class IngredientSerializer(serializers.ModelSerializer):
class Meta:
model = Ingredient
fields = '__all__'
class MealSerializer(WritableNestedModelSerializer, serializers.ModelSerializer):
ingredients_set = IngredientSerializer(required=False, many=True)
class Meta:
model = Ingredient
fields = ["id", "name","recipe", "ingredients_set"]
2nd example rewriting the create() method:
# models.py --------------------------
# Follow the same approach as the first example....
# serializers.py ----------------------
class IngredientSerializer(serializers.ModelSerializer):
class Meta:
model = Ingredient
fields = '__all__'
class MealSerializer(serializers.ModelSerializer):
ingredients = IngredientSerializer(required=False, many=True)
class Meta:
model = Meal
fields = ["id", "name","recipe", "ingredients"]
def create(self, validated_data):
# 1st step.
ingredients = validated_data('ingredients')
# 2nd step.
actual_instance = Meal.objects.create(**validated_data)
# 3rd step.
for ingredient in ingredients:
ing_objects = Ingredients.object.create(**ingredient)
actual_instance.ingredients.add(ing_objects.id)
actua_instance.save()
return actual_instance
What was done in the second example?
1st step: since you create a one-2-many relationship the endpoint will wait for a payload like this:
{
"name": null,
"recipe": null,
"ingredients": [],
}
ex of validated_data/ the data you sent:
{
"id": 1,
"name": "spaghetti",
"recipe": "recipe",
"ingredients": [
{name:'pasta',amount:100},{name:'tomato',amount:200},{...}
],
}
Therefore, since you are sending a payload with many ingredientes inside the ingredient array you will get this value from the validated_data.
For instance, if you make a print of the 'ingredients'(from the inside of the create() method) this is what you will get in your terminal:
[{name:'pasta',amount:100},{name:'tomato',amount:200},{...}]
2nd step: Alright, since you get the ingredients from validate_data it is time to create a Meal instance (where will be without the 'ingredients').
3rd step: You will loop all the ingredients objects from the 1st step you have done above and add them into the Meal.ingredients relationship saving the Meal instance.
-- about the extra model --
[2] Bear in mind that having a JSONField() allows anything to be added there even extra fields. Having a Meal model might be a better option if you want have a better control.