Django REST Framework菜单API JSON数据入库报错求助
1. 核心错误原因
报错AttributeError: 'str' object has no attribute 'get'是因为你直接遍历了整个JSON字典(json_data是包含meals和ingredients键的字典),循环中拿到的meal_data其实是字典的键名(字符串类型,比如"meals"),自然没有.get()方法。
2. 分步修复方案
2.1 修正JSON数据遍历逻辑
你需要从JSON字典中取出meals数组来遍历餐品数据,而不是直接遍历整个字典:
def store_json_data(json_data): # 先取出meals数组,再遍历 for meal_data in json_data.get('meals', []): # 后续逻辑...
2.2 修复Ingredient模型与关联逻辑
你的Ingredient模型没有meal字段,但代码中Ingredient.objects.get_or_create用了meal=meal,这会抛出字段不存在的错误。Meal和Ingredient是多对多关系,通过MealIngredient中间表关联,所以要先创建Ingredient,再创建中间表实例来关联:
# 替换原ingredient创建逻辑 ingredient, _ = Ingredient.objects.get_or_create(name=ingredient_data.get('name', '')) # 创建Meal和Ingredient的关联 MealIngredient.objects.get_or_create(meal=meal, ingredient=ingredient)
2.3 处理Ingredient的groups字段
JSON中groups是数组,但你的模型里是CharField,需要将数组转为字符串(比如逗号分隔):
# 处理全局ingredients数据时 for global_ingredient_data in json_data.get('ingredients', []): groups = ','.join(global_ingredient_data.get('groups', [])) ingredient, _ = Ingredient.objects.get_or_create( name=global_ingredient_data.get('name', ''), defaults={'groups': groups} ) # 处理options...
2.4 分离餐品与全局配料的处理逻辑
JSON里的meals字段是餐品关联的配料(仅包含名称、数量),ingredients字段是全局配料的详细信息(包含groups、options),需要分开处理:
- 先处理全局配料,保存groups和options
- 再处理餐品,关联已保存的配料
2.5 修正Option创建逻辑
Option属于全局配料,所以要从json_data['ingredients']中获取options数据,而不是餐品里的配料:
for global_ingredient_data in json_data.get('ingredients', []): ingredient, _ = Ingredient.objects.get_or_create( name=global_ingredient_data.get('name', ''), defaults={'groups': ','.join(global_ingredient_data.get('groups', []))} ) for option_data in global_ingredient_data.get('options', []): option, _ = Option.objects.get_or_create( ingredient=ingredient, quality=option_data.get('quality', ''), defaults={ 'name': option_data.get('name', ''), 'price': option_data.get('price', 0.0), 'per_amount': option_data.get('per_amount', '') } )
2.6 优化数据导入时机
不要在models.py中直接调用load_json_file(path),这会导致每次Django启动都执行导入。建议创建Django管理命令(management/commands/import_menu.py)来执行导入操作。
3. 完整修正后的代码示例
models.py(仅修正模型字段逻辑,结构不变)
from django.db import models class Meal(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=100) is_vegetarian = models.BooleanField(default=False) is_vegan = models.BooleanField(default=False) class Ingredient(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=100) groups = models.CharField(max_length=100) class Option(models.Model): id = models.AutoField(primary_key=True) ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE) name = models.CharField(max_length=100) quality = models.CharField(max_length=100) price = models.FloatField() per_amount = models.CharField(max_length=100) class MealIngredient(models.Model): id = models.AutoField(primary_key=True) meal = models.ForeignKey(Meal, on_delete=models.CASCADE) ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE)
管理命令示例(import_menu.py)
# 在app目录下创建management/commands/import_menu.py from django.core.management.base import BaseCommand import json from api.models import Meal, Ingredient, Option, MealIngredient class Command(BaseCommand): help = 'Import menu data from JSON file' def handle(self, *args, **kwargs): path = r"C:\Users\OSMAN MERT\Desktop\menu\menu_core\menu.json" with open(path) as json_file: json_data = json.load(json_file) # 先处理全局配料 for global_ingredient_data in json_data.get('ingredients', []): groups = ','.join(global_ingredient_data.get('groups', [])) ingredient, _ = Ingredient.objects.get_or_create( name=global_ingredient_data.get('name', ''), defaults={'groups': groups} ) # 处理配料选项 for option_data in global_ingredient_data.get('options', []): Option.objects.get_or_create( ingredient=ingredient, quality=option_data.get('quality', ''), defaults={ 'name': option_data.get('name', ''), 'price': option_data.get('price', 0.0), 'per_amount': option_data.get('per_amount', '') } ) # 处理餐品 for meal_data in json_data.get('meals', []): meal, created = Meal.objects.get_or_create( name=meal_data.get('name', ''), defaults={ 'is_vegetarian': meal_data.get('is_vegetarian', False), 'is_vegan': meal_data.get('is_vegan', False) } ) # 关联餐品与配料 for ingredient_data in meal_data.get('ingredients', []): ingredient = Ingredient.objects.filter(name=ingredient_data.get('name', '')).first() if ingredient: MealIngredient.objects.get_or_create(meal=meal, ingredient=ingredient) self.stdout.write(self.style.SUCCESS('Menu data imported successfully!'))
修正JSON语法错误
你提供的JSON末尾缺少闭合的]和},修正后:
{ "meals": [ { "id": 1, "name": "Rice and chicken bowl", "ingredients": [ { "name": "Rice", "quantity": 120, "quantity_type": "gram" }, { "name": "Chicken", "quantity": 85, "quantity_type": "gram" } ] }, { "id": 2, "name": "Pasta with marinara sauce and vegetables", "ingredients": [ { "name": "Pasta", "quantity": 115, "quantity_type": "gram" }, { "name": "Marinara sauce", "quantity": 120, "quantity_type": "millilitre" }, { "name": "Vegetables", "quantity": 240, "quantity_type": "gram" } ] } ], "ingredients": [ { "name": "Rice", "groups": ["vegan", "vegetarian"], "options": [ { "name": "Long grain white rice", "quality": "high", "price": 3, "per_amount": "kilogram" } ] } ] }
4. 执行导入
在项目根目录运行命令:
python manage.py import_menu
内容的提问来源于stack exchange,提问作者Mert Altintas

