Django Rest framework: сохранение связанных моделей в ModelViewSet

я пытаюсь выяснить, как сохранить связанные модели с помощью Django Rest framework. В моем приложении у меня есть модель Recipe С 2 родственные модели: RecipeIngredient и RecipeStep. А Recipe объект должен иметь не менее 3 связанных RecipeIngredient и 3 RecipeStep. До введения Rest framework я использовал Django CreateView С двумя наборами форм и процесс сохранения был следующим (следуйте коду из form_valid()):

def save_formsets(self, recipe):
    for f in self.get_formsets():
        f.instance = recipe
        f.save()

def save(self, form):
    with transaction.atomic():
        recipe = form.save()
        self.save_formsets(recipe)
    return recipe

def formsets_are_valid(self):
        return all(f.is_valid() for f in self.get_formsets())

def form_valid(self, form):
    try:
        if self.formsets_are_valid():
            try:
                return self.create_ajax_success_response(form)
            except IntegrityError as ie:
                return self.create_ajax_error_response(form, {'IntegrityError': ie.message})
    except ValidationError as ve:
        return self.create_ajax_error_response(form, {'ValidationError': ve.message})
    return self.create_ajax_error_response(form)

теперь у меня есть RecipeViewSet:

class RecipeViewSet(ModelViewSet):
    serializer_class = RecipeSerializer
    queryset = Recipe.objects.all()
    permission_classes = (RecipeModelPermission, )

использует RecipeSerializer:

class RecipeSerializer(serializers.ModelSerializer):
    class Meta:
        model = Recipe
        fields = (
            'name', 'dish_type', 'cooking_time', 'steps', 'ingredients'
        )

    ingredients = RecipeIngredientSerializer(many=True)
    steps = RecipeStepSerializer(many=True)

и это связанные сериализаторы:

class RecipeIngredientSerializer(serializers.ModelSerializer):
    class Meta:
        model = RecipeIngredient
        fields = ('name', 'quantity', 'unit_of_measure')

class RecipeStepSerializer(serializers.ModelSerializer):
    class Meta:
        model = RecipeStep
        fields = ('description', 'photo')

сейчас... как я должен проверять связанные модели (RecipeIngredient и RecipeStep) и сохранить их, когда RecipeViewSet ' s create() метод вызывается? (is_valid() на RecipeSerializer фактически игнорирует вложенные отношения и сообщает только об ошибках, связанных с основной моделью Recipe). В данный момент я попытался переопределить is_valid() метод RecipeSerializer, но это не все так просто... есть идеи?

2 ответов


Я имел дело с аналогичной проблемой на этой неделе, и я узнал, что django Rest framework 3 фактически поддерживает вложенную запись сериализации (http://www.django-rest-framework.org/topics/3.0-announcement/#serializers в вложенной сериализации подкаптера с возможностью записи.)

Я не уверен, если вложенные serialisers доступны для записи будет по умолчанию, поэтому я объявил им:

ingredients = RecipeIngredientSerializer(many=True, read_only=False)
steps = RecipeStepSerializer(many=True, read_only=False)

и вы должны переписать свой create methon внутри 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

если этот шаг структуры.объекты.create (recipe=recipe, * * step) не будет работать, возможно, вам нужно выбрать данные, представляющие каждое поле отдельно от steps_data / ingredients_data.

это ссылка на мой предыдущий (realted) вопрос / ответ в стеке:как создать несколько объектов (связанных) с одним запросом в DRF?


Я думаю, что я получу ответ.

класс RecetaSerializer (сериализаторы.ModelSerializer):

ingredientes = IngredientesSerializer(many=True, partial=True)
autor = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
depth = 2

class 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 data

def 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

важно переопределить to_internal_value (). У меня были проблемы с функцией is_valid(). Таким образом, каждое изменение в функции to_internal_value() перед функцией is_valid()