【问题标题】:Django REST framework: save related models in ModelViewSetDjango REST框架:在ModelViewSet中保存相关模型
【发布时间】:2015-04-12 01:55:47
【问题描述】:

我试图弄清楚如何使用 Django REST 框架保存相关模型。 在我的应用程序中,我有一个模型 Recipe 和 2 个相关模型:RecipeIngredientRecipeStepRecipe 对象必须至少有 3 个相关的 RecipeIngredient 和 3 个 RecipeStep。在引入 REST 框架之前,我使用带有两个表单集的 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')

现在...我应该如何验证相关模型(RecipeIngredientRecipeStep)并在调用 RecipeViewSetcreate() 方法时保存它们? (RecipeSerializer 中的is_valid() 实际上忽略了嵌套关系,只报告与主模型相关的错误Recipe)。 目前我试图覆盖RecipeSerializer中的is_valid()方法,但不是那么简单......有什么想法吗?

【问题讨论】:

    标签: django django-forms django-rest-framework formset


    【解决方案1】:

    本周我正在处理类似的问题,我发现 django rest 框架 3 实际上支持嵌套可写序列化(http://www.django-rest-framework.org/topics/3.0-announcement/#serializers 在子章可写嵌套序列化中。)

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

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

    你应该在 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
    

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

    这是我之前在堆栈上的(真实的)问题/答案的链接:How to create multiple objects (related) with one request in DRF?

    【讨论】:

    • 感谢您的提示,但这并不是那么简单...我在 valid_data 中得到一个空的成分列表
    • 这可能意味着,您的序列化程序不知道如何根据您发送给它的数据创建嵌套关系。您确定以正确的格式/结构发送数据吗?
    • 这实际上是我的疑问:P 我正在发送一个 json,例如:{"ingredients": [{"name": "the name", "quantity": "the quantity", "unit_of_measure": "the unit"}, ...]}... 应该没问题... 还是不行?
    • 我实际上是在传递一个字符串而不是一个 json (我正在使用 Django 的测试客户端,现在我切换到 APIClient)......它工作得非常完美!谢谢你:)
    • ps。无需指定read_only=False,因为它是默认值
    【解决方案2】:

    我想我得到了答案。

    类 RecetaSerializer(serializers.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() 之前

    【讨论】:

      猜你喜欢
      • 2018-07-21
      • 1970-01-01
      • 2019-04-26
      • 2017-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多