Django REST框架:在ModelViewSet中保存相关模型

dav*_*ode 10 django django-forms formset django-rest-framework

我正试图弄清楚如何使用Django REST框架保存相关模型.在我的应用程序中,我有一个模型Recipe与2相关模型:RecipeIngredientRecipeStep.一个Recipe对象必须至少有3个相关的RecipeIngredient和3个RecipeStep.在引入REST框架之前,我使用了一个CreateView带有两个formset 的Django ,并且保存过程如下(遵循代码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)
Run Code Online (Sandbox Code Playgroud)

现在我有了RecipeViewSet:

class RecipeViewSet(ModelViewSet):
    serializer_class = RecipeSerializer
    queryset = Recipe.objects.all()
    permission_classes = (RecipeModelPermission, )
Run Code Online (Sandbox Code Playgroud)

使用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)
Run Code Online (Sandbox Code Playgroud)

这些是相关的序列化器:

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')
Run Code Online (Sandbox Code Playgroud)

现在......怎么我应该来验证相关模型(RecipeIngredientRecipeStep),并将它们保存时RecipeViewSetcreate()方法被调用?(is_valid()RecipeSerializer实际上无视嵌套关系和报告只涉及主模型误差Recipe).目前我试图覆盖该is_valid()方法RecipeSerializer,但不是那么简单......任何想法?

Mat*_*tko 17

我本周正在处理类似的问题而且我发现,django rest framework 3实际上支持嵌套的可写序列化(http://www.django-rest-framework.org/topics/3.0-announcement/#serializers in subchapter Writable nested序列化.)

我不确定嵌套的序列化程序是否可写是默认的,所以我声明了它们:

ingredients = RecipeIngredientSerializer(many=True, read_only=False)
steps = RecipeStepSerializer(many=True, read_only=False)
Run Code Online (Sandbox Code Playgroud)

你应该在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
Run Code Online (Sandbox Code Playgroud)

如果这个结构Step.objects.create(recipe = recipe,**step)不起作用,也许你必须从steps_data/ingredients_data中分别选择代表每个字段的数据.

这是我在堆栈上的早期(实际)问题/答案的链接:如何在DRF中使用一个请求创建多个对象(相关)?

  • 我实际上是传递一个字符串而不是一个json(我现在使用的是Django的测试客户端,我现在切换到了APIClient)...它的工作完美!谢谢 :) (2认同)