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()