You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DRF保存模型触发AttributeError:Ingredient无ingredient属性

DRF保存模型时AttributeError错误排查

错误信息

Got AttributeError when attempting to get a value for field id on serializer IngredientSerializer. The serializer field might be named incorrectly and not match any attribute or key on the Ingredient instance. Original exception text was: 'Ingredient' object has no attribute 'ingredient'.

使用普通序列化器和ModelSerializer均触发该错误,当前Django版本为3.2.25。

相关代码

序列化器代码

from django.db import transaction
from rest_framework import serializers
from .models import Recipe, Ingredient, RecipeIngredient, Tag


class IngredientSerializer(serializers.ModelSerializer):
    id = serializers.PrimaryKeyRelatedField(queryset=Ingredient.objects.all, source="ingredient")
    amount = serializers.IntegerField()

    class Meta:
        model = RecipeIngredient
        fields = ["id", "amount"]


class CreateRecipeSerializer(serializers.ModelSerializer):
    ingredients = IngredientSerializer(many=True)
    tags=serializers.PrimaryKeyRelatedField(...)
    image = serializers.ImageField(required=False)
    name = serializers.CharField(source="recipe_name")

    class Meta:
        model = Recipe
        fields = ["name", "image", "text", "cooking_time", "ingredients", "tags"]

    def create(self, validated_data):
        ingredients_data = validated_data.pop("ingredients")
        tags_data = validated_data.pop("tags")

        with transaction.atomic():
            recipe = Recipe.objects.create(**validated_data)
            recipe.tags.set(tags_data)

            recipe_ingredients = [
                RecipeIngredient(
                    recipe=recipe,
                    ingredient=ingredient_data["ingredient"],
                    amount=ingredient_data["amount"],
                    )
                     for ingredient_data in ingredients_data]

            RecipeIngredient.objects.bulk_create(recipe_ingredients)

        return recipe

模型代码

class Ingredient(models.Model):
  
    ingredient_name = models.CharField(max_length=MAX_LENGTH_200)
    measurement_unit = models.CharField(max_length=MAX_LENGTH_200)


class Recipe(models.Model):
    ingredients = models.ManyToManyField(Ingredient, through="RecipeIngredient", related_name="recipes")
    tags = models.ManyToManyField(Tag, related_name="recipes")
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="recipes")
    recipe_name = models.CharField(max_length=MAX_LENGTH_200)
    image = models.ImageField(upload_to="recipes/images/")
    text = models.TextField()
    cooking_time = models.PositiveIntegerField(validators=[MinValueValidator(1),MaxValueValidator(50000)])


class RecipeIngredient(models.Model):
    recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name="recipe_ingredients")
    ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE, related_name="ingredients")
    amount = models.PositiveIntegerField()

错误根源

  1. CreateRecipeSerializer绑定的模型是Recipe,其ingredients字段是关联Ingredient的多对多字段,但你将该字段指定为IngredientSerializer(绑定RecipeIngredient模型),序列化逻辑冲突。
  2. 当DRF序列化Recipe实例返回时,会把Ingredient对象传入IngredientSerializer,而该序列化器中定义的id = serializers.PrimaryKeyRelatedField(..., source="ingredient")会尝试从Ingredient对象中获取ingredient属性——但Ingredient模型不存在该字段,因此抛出AttributeError。
  3. 另外,queryset=Ingredient.objects.all缺少括号,应改为queryset=Ingredient.objects.all(),否则传递的是方法对象而非查询集。

修复方案

方案一:仅用嵌套序列化器处理创建输入(推荐)

将CreateRecipeSerializer中的ingredients字段设为write_only=True,仅用于接收创建时的配料数据,返回时使用Recipe模型默认的ingredients序列化逻辑:

class IngredientSerializer(serializers.ModelSerializer):
    id = serializers.PrimaryKeyRelatedField(queryset=Ingredient.objects.all(), source='ingredient')
    amount = serializers.IntegerField()

    class Meta:
        model = RecipeIngredient
        fields = ["id", "amount"]


class CreateRecipeSerializer(serializers.ModelSerializer):
    # 仅用于创建时接收数据,序列化返回时忽略
    ingredients = IngredientSerializer(many=True, write_only=True)
    tags = serializers.PrimaryKeyRelatedField(queryset=Tag.objects.all(), many=True)
    image = serializers.ImageField(required=False)
    name = serializers.CharField(source="recipe_name")

    class Meta:
        model = Recipe
        fields = ["name", "image", "text", "cooking_time", "ingredients", "tags"]

    def create(self, validated_data):
        ingredients_data = validated_data.pop("ingredients")
        tags_data = validated_data.pop("tags")

        with transaction.atomic():
            recipe = Recipe.objects.create(**validated_data)
            recipe.tags.set(tags_data)

            recipe_ingredients = [
                RecipeIngredient(
                    recipe=recipe,
                    ingredient=ingredient_data["ingredient"],
                    amount=ingredient_data["amount"],
                ) for ingredient_data in ingredients_data
            ]

            RecipeIngredient.objects.bulk_create(recipe_ingredients)

        return recipe

方案二:返回时包含配料数量

如果需要在返回Recipe时同时带出配料的使用数量,可自定义序列化逻辑,通过关联的RecipeIngredient获取数据:

class RecipeIngredientSerializer(serializers.ModelSerializer):
    id = serializers.PrimaryKeyRelatedField(source='ingredient.id', read_only=True)
    name = serializers.CharField(source='ingredient.ingredient_name', read_only=True)
    amount = serializers.IntegerField()

    class Meta:
        model = RecipeIngredient
        fields = ["id", "name", "amount"]


class CreateRecipeSerializer(serializers.ModelSerializer):
    # 仅用于创建时接收数据
    ingredients = serializers.ListField(
        child=serializers.DictField(
            child=serializers.CharField(),
        ),
        write_only=True
    )
    # 返回时展示带数量的配料信息
    recipe_ingredients = RecipeIngredientSerializer(source='recipe_ingredients', many=True, read_only=True)
    tags = serializers.PrimaryKeyRelatedField(queryset=Tag.objects.all(), many=True)
    image = serializers.ImageField(required=False)
    name = serializers.CharField(source="recipe_name")

    class Meta:
        model = Recipe
        fields = ["name", "image", "text", "cooking_time", "ingredients", "tags", "recipe_ingredients"]

    def create(self, validated_data):
        ingredients_data = validated_data.pop("ingredients")
        tags_data = validated_data.pop("tags")

        with transaction.atomic():
            recipe = Recipe.objects.create(**validated_data)
            recipe.tags.set(tags_data)

            recipe_ingredients = []
            for item in ingredients_data:
                ingredient = Ingredient.objects.get(id=item['id'])
                recipe_ingredients.append(
                    RecipeIngredient(
                        recipe=recipe,
                        ingredient=ingredient,
                        amount=int(item['amount']),
                    )
                )

            RecipeIngredient.objects.bulk_create(recipe_ingredients)

        return recipe

内容的提问来源于stack exchange,提问作者SerhiiL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 10:58:08