Django REST framework: save related models in ModelViewSet
I was dealing with similiar issue this week and I found out, that django rest framework 3 actually supports nested writable serialisation (http://www.django-rest-framework.org/topics/3.0-announcement/#serializers in subchapter Writable nested serialization.)
Im not sure if nested serialisers are writable be default, so I declared them:
ingredients = RecipeIngredientSerializer(many=True, read_only=False)steps = RecipeStepSerializer(many=True, read_only=False)
and you should rewrite your create methon inside RecipeSerializer:
class RecipeSerializer(serializers.ModelSerializer): ingredients = RecipeIngredientSerializer(many=True, read_only=False) steps = RecipeStepSerializer(many=True, read_only=False) class Meta: model = Recipe fields = ( 'name', 'dish_type', 'cooking_time', 'steps', 'ingredients' ) def create(self, validated_data): ingredients_data = validated_data.pop('ingredients') steps_data = validated_data.pop('steps') recipe = Recipe.objects.create(**validated_data) for ingredient in ingredients_data: #any ingredient logic here Ingredient.objects.create(recipe=recipe, **ingredient) for step in steps_data: #any step logic here Step.objects.create(recipe=recipe, **step) return recipe
if this structure Step.objects.create(recipe=recipe, **step) wont work, maybe you have to select data representeng each field separatly from steps_data / ingredients_data.
This is link to my earlier (realted) question/answer on stack: How to create multiple objects (related) with one request in DRF?
I think that I get the answer.
class RecetaSerializer(serializers.ModelSerializer):
ingredientes = IngredientesSerializer(many=True, partial=True)autor = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())depth = 2class Meta: model = Receta fields = ('url','pk','nombre','foto','sabias_que','ingredientes','pasos','fecha_publicacion','autor') def to_internal_value(self,data): data["fecha_publicacion"] = timezone.now() ingredientes_data = data["ingredientes"] for ingrediente in ingredientes_data: alimento_data = ingrediente["alimento"] if Alimento.objects.filter(codigo = alimento_data['codigo']).exists(): alimento = Alimento.objects.get(codigo= alimento_data['codigo']) ingrediente["alimento"] = alimento else: alimento = Alimento(codigo = alimento_data['codigo'], nombre = alimento_data['nombre']) alimento.save() ingrediente["alimento"] = alimento data["ingredientes"] = ingredientes_data return datadef create(self, validated_data): ingredientes_data = validated_data.pop('ingredientes') receta_data = validated_data usuario = User.objects.get(id = validated_data["autor"]) receta_data['autor'] = usuario receta = Receta.objects.create(**validated_data) for ingrediente in ingredientes_data: alimento_data = ingrediente["alimento"] ingrediente = Ingredientes(receta= receta, cantidad = ingrediente['cantidad'], unidad = ingrediente['unidad'], alimento = alimento_data) ingrediente.save() receta.save() return receta
It's important to override to_internal_value(). I had problems with the function is_valid(). So every change make in the function to_internal_value() is before the function is_valid()