学校订餐菜单生成器:Django+DRF 后端与 React 前端,支持 A/B 双套营养周菜单生成、部分自选、拼音搜索、营养校准、Excel 导出
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
"""按中国食物成分表校准菜品营养成分。
|
||||
|
||||
原理:解析每道菜配料明细中的食材与克数,乘以对应食材营养密度(每 100g),
|
||||
再按烹饪方式(炒/红烧/油炸/蒸煮)修正油脂与糖的用量,得到更符合实际的值。
|
||||
用法:python manage.py calibrate_nutrition [--apply] [--export-seed]
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from meals.models import Dish
|
||||
|
||||
# 每 100g 可食部:蛋白质g, 脂肪g, 热量kcal(参考中国食物成分表/常见值)
|
||||
NUTRITION = {
|
||||
'五花肉': (9.5, 35.0, 400), '猪五花': (9.5, 35.0, 400),
|
||||
'梅花肉': (20.0, 8.0, 150), '里脊肉': (20.3, 6.2, 143), '瘦肉': (20.3, 6.2, 143),
|
||||
'肉丝': (20.0, 7.0, 150), '肉片': (20.0, 8.0, 155), '肉糜': (18.0, 12.0, 180),
|
||||
'肉馅': (18.0, 12.0, 180), '肉末': (18.0, 12.0, 180),
|
||||
'猪大排': (12.6, 12.6, 168), '大排': (12.6, 12.6, 168),
|
||||
'肋排': (12.8, 14.3, 185), '排骨': (12.8, 14.3, 185), '小排': (12.8, 14.3, 185),
|
||||
'汤骨': (15.0, 15.0, 220), '牛腩': (20.0, 8.0, 150), '牛肉': (20.2, 4.2, 125),
|
||||
'羊肉': (19.0, 14.1, 203), '鸡胸': (24.0, 1.5, 118), '鸡丁': (19.0, 6.0, 140),
|
||||
'鸡块': (19.0, 8.0, 150), '鸡肉': (19.3, 9.4, 167), '鸡腿': (14.0, 9.0, 150),
|
||||
'鸡翅': (17.4, 11.8, 194), '烤翅': (19.0, 16.0, 240), '鸭腿': (16.0, 18.0, 230),
|
||||
'鸭块': (15.5, 19.7, 240), '鸭肉': (15.5, 19.7, 240),
|
||||
'鱼片': (17.0, 4.0, 110), '鱼块': (16.6, 5.2, 113), '草鱼': (16.6, 5.2, 113),
|
||||
'鲈鱼': (18.6, 3.4, 105), '鲫鱼': (17.1, 2.7, 108),
|
||||
'基围虾': (18.6, 0.8, 93), '虾仁': (18.6, 0.8, 93), '虾皮': (30.7, 2.2, 153),
|
||||
'鸡蛋': (13.3, 8.8, 144), '咸蛋黄': (16.0, 30.0, 330), '皮蛋': (14.2, 10.7, 171),
|
||||
'豆腐': (8.1, 3.7, 81), '素鸡': (16.5, 12.5, 192), '烤麸': (22.5, 0.7, 121),
|
||||
'百叶结': (24.5, 9.0, 205), '油面筋': (17.1, 17.6, 244), '油方': (17.1, 17.6, 244),
|
||||
'腐竹': (44.6, 21.7, 459), '粉丝': (0.8, 0.2, 338), '花生米': (24.1, 44.4, 574),
|
||||
'花生': (24.1, 44.4, 574), '贡丸': (12.0, 12.0, 200), '红肠': (12.0, 15.0, 220),
|
||||
'烤肠': (10.0, 20.0, 230),
|
||||
'土豆': (2.0, 0.2, 77), '红薯': (1.1, 0.2, 90), '南瓜': (0.7, 0.1, 23),
|
||||
'山药': (1.9, 0.2, 57), '玉米棒': (4.0, 1.2, 112), '玉米': (4.0, 1.2, 112),
|
||||
'冬瓜': (0.4, 0.2, 12), '西葫芦': (0.8, 0.2, 19), '丝瓜': (1.0, 0.2, 20),
|
||||
'番茄': (0.9, 0.2, 20), '西红柿': (0.9, 0.2, 20), '茄子': (1.1, 0.2, 23),
|
||||
'青椒': (1.0, 0.2, 22), '辣椒': (1.3, 0.4, 32), '干辣椒': (15.0, 12.0, 212),
|
||||
'彩椒': (1.0, 0.2, 22), '大白菜': (1.5, 0.1, 17), '白菜': (1.5, 0.1, 17),
|
||||
'卷心菜': (1.5, 0.2, 22), '牛心菜': (1.5, 0.2, 22), '包菜': (1.5, 0.2, 24),
|
||||
'娃娃菜': (1.7, 0.3, 15), '青菜': (1.7, 0.3, 15), '菠菜': (2.6, 0.3, 28),
|
||||
'空心菜': (2.2, 0.3, 20), '菜心': (2.8, 0.5, 22), '生菜': (1.3, 0.3, 15),
|
||||
'杭白菜': (1.8, 0.4, 18), '茼蒿菜': (1.9, 0.3, 24), '茼蒿': (1.9, 0.3, 24),
|
||||
'莴笋': (1.0, 0.1, 15), '芦笋': (2.6, 0.2, 22), '西兰花': (4.1, 0.6, 36),
|
||||
'莲藕': (1.9, 0.2, 73), '藕片': (1.9, 0.2, 73), '藕丁': (1.9, 0.2, 73),
|
||||
'茭白': (1.2, 0.2, 26), '蚕豆': (8.8, 0.4, 104), '荷兰豆': (2.5, 0.3, 30),
|
||||
'四季豆': (2.0, 0.4, 31), '豆芽': (2.1, 0.1, 18), '香菇': (2.2, 0.3, 26),
|
||||
'菌菇': (2.5, 0.3, 25), '木耳': (1.5, 0.2, 27), '紫菜': (26.7, 1.1, 250),
|
||||
'海带': (1.2, 0.1, 13), '黄瓜': (0.8, 0.2, 16), '洋葱': (1.1, 0.2, 40),
|
||||
'大葱': (1.7, 0.3, 30), '韭菜': (2.4, 0.4, 26), '胡萝卜': (1.0, 0.2, 39),
|
||||
'马蹄': (1.2, 0.2, 61), '荸荠': (1.2, 0.2, 61), '酸菜': (1.2, 0.2, 18),
|
||||
'蒜苗': (2.1, 0.4, 37), '豆豉': (24.0, 8.0, 220), '葱油': (0.0, 100.0, 900),
|
||||
'笋丝': (2.6, 0.2, 25), '笋': (2.6, 0.2, 25), '芹菜': (1.2, 0.2, 22),
|
||||
'红枣': (3.2, 0.5, 276), '剁椒': (2.0, 1.0, 40), '荸荠': (1.2, 0.2, 61),
|
||||
'米饭': (2.6, 0.3, 116), '黑米饭': (3.2, 0.6, 122), '麦片饭': (4.0, 1.2, 130),
|
||||
'杂粮饭': (3.5, 0.8, 125), '炒面': (4.5, 6.5, 175), '豆沙包': (8.5, 3.0, 220),
|
||||
'奶黄包': (7.0, 4.0, 200), '小笼包': (9.0, 5.0, 220), '花卷': (7.0, 1.0, 220),
|
||||
'馒头': (7.0, 1.0, 220),
|
||||
}
|
||||
|
||||
# 非克单位 → 每单位可食部克数
|
||||
UNIT_GRAMS = {
|
||||
'只': {'鸡翅': 30, '烤翅': 35, '鸡蛋': 50, '蛋': 50, '基围虾': 10, '虾': 10,
|
||||
'鸡腿': 100, '鸭腿': 100, '油面筋': 20, '油方': 20, '小笼包': 25},
|
||||
'根': {'烤肠': 50, '火腿肠': 50},
|
||||
'块': {'大排': 70, '排骨': 100, '鱼块': 100, '鸡块': 35, '素鸡': 50, '豆腐': 100},
|
||||
}
|
||||
UNIT_DEFAULT = {'只': 30, '根': 50, '块': 80}
|
||||
|
||||
# 烹饪方式修正(额外油/糖,每份):加 蛋白质g、脂肪g、热量kcal
|
||||
COOK_OIL = [
|
||||
(('油炸', '炸'), (0.0, 12.0, 108.0)),
|
||||
(('地三鲜', '水煮', '回锅'), (0.0, 8.0, 72.0)),
|
||||
(('红烧', '糖醋', '蜜汁', '拔丝', '可乐', '鱼香', '酱爆', '酱鸭', '东坡', '焖', '油焖'), (0.0, 5.0, 80.0)),
|
||||
(('炒', '爆', '干煸', '蒜香', '烤', '焗'), (0.0, 5.0, 45.0)),
|
||||
(('蒸', '炖', '煮', '白灼', '凉拌', '汤', '清炒', '蒜蓉'), (0.0, 2.0, 18.0)),
|
||||
(('糖拌',), (0.0, 0.0, 45.0)),
|
||||
(('塞肉',), (4.5, 3.0, 45.0)),
|
||||
]
|
||||
|
||||
# 类型合理范围 clamp:(protein, fat, kcal)
|
||||
CLAMP = {
|
||||
'meat': ((4.0, 20.0), (2.0, 28.0), (90.0, 340.0)),
|
||||
'veg': ((1.0, 10.0), (0.5, 12.0), (35.0, 170.0)),
|
||||
'soup': ((1.5, 7.0), (1.0, 7.0), (30.0, 100.0)),
|
||||
'staple': ((2.0, 9.0), (0.5, 10.0), (70.0, 320.0)),
|
||||
}
|
||||
|
||||
|
||||
def _parse_items(text):
|
||||
"""解析 '食材12g食材34g' / '食材2只' → [(食材名, 克数)]"""
|
||||
items = []
|
||||
for m in re.finditer(r'([\u4e00-\u9fa5]{1,10}?)(\d+\.?\d*)g', text):
|
||||
items.append((m.group(1), float(m.group(2))))
|
||||
for m in re.finditer(r'([\u4e00-\u9fa5]{1,10}?)(\d+)(?:-(\d+))?(只|根|块)', text):
|
||||
name, unit = m.group(1), m.group(4)
|
||||
n = int(m.group(3) or m.group(2))
|
||||
grams = UNIT_GRAMS.get(unit, {}).get(name)
|
||||
if grams is None:
|
||||
grams = next((v for k, v in UNIT_GRAMS.get(unit, {}).items() if k in name), UNIT_DEFAULT[unit])
|
||||
items.append((name, grams * n))
|
||||
return items
|
||||
|
||||
|
||||
def _food_nutrition(name):
|
||||
for key in sorted(NUTRITION, key=len, reverse=True):
|
||||
if key in name:
|
||||
return NUTRITION[key]
|
||||
return None
|
||||
|
||||
|
||||
def _cook_fix(name):
|
||||
for keywords, fix in COOK_OIL:
|
||||
for k in keywords:
|
||||
if k in name:
|
||||
return fix
|
||||
return (0.0, 3.0, 27.0)
|
||||
|
||||
|
||||
def _calc(name, dish_type, text):
|
||||
items = _parse_items(text)
|
||||
protein = fat = kcal = 0.0
|
||||
matched = 0
|
||||
for food, grams in items:
|
||||
n = _food_nutrition(food)
|
||||
if n:
|
||||
matched += 1
|
||||
p, f, k = n
|
||||
protein += p * grams / 100
|
||||
fat += f * grams / 100
|
||||
kcal += k * grams / 100
|
||||
if not matched:
|
||||
return None
|
||||
if dish_type != 'staple':
|
||||
fix = _cook_fix(name)
|
||||
fat += fix[1]
|
||||
kcal += fix[2]
|
||||
(p_min, p_max), (f_min, f_max), (k_min, k_max) = CLAMP[dish_type]
|
||||
protein = min(max(protein, p_min), p_max)
|
||||
fat = min(max(fat, f_min), f_max)
|
||||
kcal = min(max(round(kcal), int(k_min)), int(k_max))
|
||||
return round(protein, 1), round(fat, 1), int(kcal)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '按食物成分表校准菜品营养成分(--apply 写入数据库,--export-seed 重新生成 seed_dishes.py)'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--apply', action='store_true', help='写入数据库(默认仅预览)')
|
||||
parser.add_argument('--export-seed', action='store_true', help='重新生成 seed_dishes.py')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
apply_changes = options['apply']
|
||||
updated, unchanged, skipped = [], [], []
|
||||
rows = []
|
||||
for dish in Dish.objects.filter(is_active=True).order_by('dish_type', 'cuisine', 'id'):
|
||||
result = _calc(dish.name, dish.dish_type, dish.ingredient_detail or '')
|
||||
if result is None:
|
||||
skipped.append(dish.name)
|
||||
continue
|
||||
p, f, k = result
|
||||
old = (float(dish.protein), float(dish.fat), int(dish.calorie))
|
||||
rows.append((dish, old, result))
|
||||
if abs(old[0] - p) < 0.05 and abs(old[1] - f) < 0.05 and old[2] == k:
|
||||
unchanged.append(dish.name)
|
||||
continue
|
||||
updated.append(dish.name)
|
||||
self.stdout.write(
|
||||
f'{dish.name}: 蛋白 {old[0]}→{p} | 脂肪 {old[1]}→{f} | 热量 {old[2]}→{k}'
|
||||
)
|
||||
|
||||
self.stdout.write(self.style.WARNING(f'\n将更新 {len(updated)} 道,不变 {len(unchanged)} 道,无法解析 {len(skipped)} 道'))
|
||||
if skipped:
|
||||
self.stdout.write('无法解析(保留原值): ' + '、'.join(skipped))
|
||||
|
||||
if apply_changes:
|
||||
for dish, _, (p, f, k) in rows:
|
||||
dish.protein, dish.fat, dish.calorie = p, f, k
|
||||
dish.save(update_fields=['protein', 'fat', 'calorie'])
|
||||
self.stdout.write(self.style.SUCCESS('已写入数据库'))
|
||||
|
||||
if options['export_seed']:
|
||||
self._export_seed(rows, skipped)
|
||||
|
||||
def _export_seed(self, rows, skipped):
|
||||
path = __file__.replace('calibrate_nutrition.py', 'seed_dishes.py')
|
||||
grouped = {}
|
||||
for dish, _, (p, f, k) in rows:
|
||||
grouped.setdefault(dish.dish_type, []).append(dish)
|
||||
lines = []
|
||||
lines.append('from django.core.management.base import BaseCommand')
|
||||
lines.append('')
|
||||
lines.append('from meals.models import Dish')
|
||||
lines.append('')
|
||||
lines.append('# (菜名, 菜系, 配料明细, 蛋白质g, 脂肪g, 热量kcal)')
|
||||
lines.append('SAMPLE_DISHES = {')
|
||||
for t in ('meat', 'veg', 'soup', 'staple'):
|
||||
dishes = [d for d in grouped.get(t, []) if d.name not in skipped]
|
||||
lines.append(f" '{t}': [")
|
||||
for d in dishes:
|
||||
lines.append(
|
||||
f" ('{d.name}', '{d.cuisine}', '{d.ingredient_detail}', {d.protein}, {d.fat}, {d.calorie}),"
|
||||
)
|
||||
lines.append(' ],')
|
||||
lines.append('}')
|
||||
tail = '''
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '写入示例菜品数据(含菜系)'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
count = 0
|
||||
for dish_type, items in SAMPLE_DISHES.items():
|
||||
for name, cuisine, ingredient, protein, fat, calorie in items:
|
||||
_, created = Dish.objects.update_or_create(
|
||||
name=name,
|
||||
defaults={
|
||||
'dish_type': dish_type,
|
||||
'cuisine': cuisine,
|
||||
'ingredient_detail': ingredient,
|
||||
'protein': protein,
|
||||
'fat': fat,
|
||||
'calorie': calorie,
|
||||
},
|
||||
)
|
||||
if created:
|
||||
count += 1
|
||||
self.stdout.write(self.style.SUCCESS(f'示例菜品已就绪(新增 {count} 道,库中共 {Dish.objects.count()} 道)'))
|
||||
'''
|
||||
with open(path, 'w', encoding='utf-8') as f:
|
||||
f.write('\n'.join(lines) + tail)
|
||||
self.stdout.write(self.style.SUCCESS(f'seed_dishes.py 已重新生成:{path}'))
|
||||
@@ -0,0 +1,53 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from meals.models import Dish
|
||||
|
||||
TYPE_MAP = {'荤菜': 'meat', '素菜': 'veg', '汤': 'soup', '主食': 'staple'}
|
||||
TYPE_MAP_REV = {v: k for k, v in TYPE_MAP.items()}
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '从文本文件批量导入菜品。每行格式:类型|菜名|配料明细|蛋白质|脂肪|热量|菜系 (类型:荤菜/素菜/汤/主食,营养与菜系可省略)'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('file', help='菜品文本文件路径')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
path = options['file']
|
||||
created, skipped = 0, 0
|
||||
with open(path, encoding='utf-8-sig') as f:
|
||||
for line_no, line in enumerate(f, 1):
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
parts = [p.strip() for p in line.split('|')]
|
||||
if len(parts) < 2:
|
||||
self.stderr.write(f'第 {line_no} 行格式错误,已跳过: {line}')
|
||||
skipped += 1
|
||||
continue
|
||||
label, name = parts[0], parts[1]
|
||||
dish_type = TYPE_MAP.get(label)
|
||||
if not dish_type or not name:
|
||||
self.stderr.write(f'第 {line_no} 行类型或菜名无效,已跳过: {line}')
|
||||
skipped += 1
|
||||
continue
|
||||
ingredient = parts[2] if len(parts) > 2 else ''
|
||||
protein = float(parts[3]) if len(parts) > 3 and parts[3] else 0
|
||||
fat = float(parts[4]) if len(parts) > 4 and parts[4] else 0
|
||||
calorie = int(float(parts[5])) if len(parts) > 5 and parts[5] else 0
|
||||
cuisine = parts[6] if len(parts) > 6 and parts[6] else '家常菜'
|
||||
dish, is_created = Dish.objects.update_or_create(
|
||||
name=name,
|
||||
defaults={
|
||||
'dish_type': dish_type,
|
||||
'cuisine': cuisine,
|
||||
'ingredient_detail': ingredient,
|
||||
'protein': protein,
|
||||
'fat': fat,
|
||||
'calorie': calorie,
|
||||
},
|
||||
)
|
||||
if is_created:
|
||||
created += 1
|
||||
self.stdout.write(f'新增: {label} - {name}({cuisine})')
|
||||
self.stdout.write(self.style.SUCCESS(f'导入完成:新增 {created} 道,跳过 {skipped} 条'))
|
||||
@@ -0,0 +1,165 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from meals.models import Dish
|
||||
|
||||
# (菜名, 菜系, 配料明细, 蛋白质g, 脂肪g, 热量kcal)
|
||||
SAMPLE_DISHES = {
|
||||
'meat': [
|
||||
('可乐鸡翅', '家常菜', '鸡翅2只', 10.4, 12.1, 196),
|
||||
('奥尔良烤翅', '家常菜', '烤翅2只', 13.3, 16.2, 213),
|
||||
('红烧三角油方塞肉', '家常菜', '三角油方1只', 4.0, 8.5, 129),
|
||||
('牛心菜炒五花肉片', '家常菜', '牛心菜85g五花肉片15g', 4.0, 10.4, 124),
|
||||
('五花肉烧百叶结', '家常菜', '五花肉85g百叶结20g', 13.0, 28.0, 340),
|
||||
('火山石烤肠', '家常菜', '烤肠1根', 5.0, 15.0, 160),
|
||||
('肉糜蒸蛋', '家常菜', '肉糜65g鸡蛋25g', 15.0, 12.0, 171),
|
||||
('蚝油牛肉片', '家常菜', '牛肉片85g洋葱50g', 17.6, 9.9, 179),
|
||||
('原味鸡块', '家常菜', '鸡块2块', 13.3, 8.6, 132),
|
||||
('青椒茭白肉丝', '家常菜', '青椒5g茭白75g肉丝15g', 4.0, 4.2, 90),
|
||||
('蒜香骨', '家常菜', '肋排100g', 12.8, 19.3, 230),
|
||||
('番茄牛腩', '家常菜', '牛腩80g番茄70g', 16.6, 9.5, 161),
|
||||
('土豆烧鸡块', '家常菜', '鸡块100g土豆60g', 20.0, 11.1, 223),
|
||||
('青椒肉丝', '家常菜', '肉丝70g青椒40g', 14.4, 8.0, 141),
|
||||
('洋葱炒肉片', '家常菜', '肉片70g洋葱50g', 14.6, 10.7, 174),
|
||||
('香菇滑鸡', '家常菜', '鸡块110g香菇20g', 20.0, 11.9, 197),
|
||||
('冬瓜烧排骨', '家常菜', '排骨80g冬瓜60g', 10.5, 14.6, 182),
|
||||
('家常回锅肉', '家常菜', '五花肉80g蒜苗30g', 8.2, 28.0, 340),
|
||||
('木须肉', '家常菜', '肉片50g鸡蛋30g木耳10g黄瓜20g', 14.3, 9.7, 154),
|
||||
('红烧鸡腿', '家常菜', '鸡腿1只', 14.0, 14.0, 230),
|
||||
('玉米炖排骨', '家常菜', '排骨80g玉米70g', 13.0, 14.3, 244),
|
||||
('啤酒鸭', '家常菜', '鸭块120g青椒30g', 18.9, 26.7, 322),
|
||||
('宫保鸡丁', '川菜', '鸡丁80g花生米20g', 20.0, 16.7, 254),
|
||||
('鱼香肉丝', '川菜', '肉丝70g笋丝30g', 14.8, 10.0, 192),
|
||||
('辣子鸡丁', '川菜', '鸡丁100g干辣椒10g', 20.0, 10.2, 188),
|
||||
('水煮肉片', '川菜', '肉片90g豆芽40g', 18.8, 15.2, 219),
|
||||
('酸菜鱼', '川菜', '鱼片100g酸菜50g', 17.6, 7.1, 146),
|
||||
('回锅肉', '川菜', '五花肉85g蒜苗25g', 8.6, 28.0, 340),
|
||||
('蚂蚁上树', '川菜', '粉丝50g肉末30g', 5.8, 6.7, 250),
|
||||
('鱼香茄子煲', '川菜', '茄子100g肉末25g', 5.6, 8.2, 148),
|
||||
('红烧肉', '本帮菜', '五花肉100g', 9.5, 28.0, 340),
|
||||
('糖醋排骨', '本帮菜', '肋排100g', 12.8, 19.3, 265),
|
||||
('红烧狮子头', '本帮菜', '肉糜80g马蹄20g', 14.6, 14.6, 236),
|
||||
('油面筋塞肉', '本帮菜', '油面筋2只肉馅50g', 15.8, 16.0, 233),
|
||||
('酱鸭', '本帮菜', '鸭腿150g', 20.0, 28.0, 340),
|
||||
('葱油鸡', '本帮菜', '鸡腿150g葱油10g', 20.0, 26.5, 340),
|
||||
('熏鱼', '本帮菜', '草鱼块100g', 16.6, 8.2, 140),
|
||||
('百叶结烧肉', '本帮菜', '五花肉80g百叶结30g', 14.9, 28.0, 340),
|
||||
('红烧基围虾', '江浙菜', '基围虾3-4只', 7.4, 5.3, 117),
|
||||
('白灼虾', '江浙菜', '基围虾4只', 7.4, 2.3, 90),
|
||||
('清蒸狮子头', '江浙菜', '肉糜80g荸荠20g', 14.6, 11.6, 174),
|
||||
('西湖醋鱼', '江浙菜', '草鱼块120g', 19.9, 9.2, 163),
|
||||
('东坡肉', '江浙菜', '五花肉90g', 8.6, 28.0, 340),
|
||||
('小炒黄牛肉', '湘菜', '牛肉片80g芹菜30g', 16.4, 11.5, 176),
|
||||
('农家小炒肉', '湘菜', '五花肉70g青椒50g', 7.2, 28.0, 336),
|
||||
('辣椒炒肉', '湘菜', '瘦肉60g青椒60g', 12.8, 8.8, 144),
|
||||
('剁椒鱼块', '湘菜', '鱼块110g剁椒15g', 18.6, 8.9, 157),
|
||||
('白切鸡', '粤菜', '鸡腿150g姜葱适量', 20.0, 16.5, 252),
|
||||
('豉汁蒸排骨', '粤菜', '肋排100g豆豉5g', 14.0, 16.7, 214),
|
||||
('清蒸鲈鱼', '粤菜', '鲈鱼块120g', 19.9, 8.2, 154),
|
||||
('蒜蓉粉丝蒸虾', '粤菜', '基围虾4只粉丝20g', 7.6, 2.4, 123),
|
||||
('蜜汁叉烧', '粤菜', '梅花肉100g', 20.0, 13.0, 230),
|
||||
('蜜汁鸡腿', '粤菜', '鸡腿1只', 14.0, 14.0, 230),
|
||||
('虾仁滑蛋', '粤菜', '虾仁50g鸡蛋60g', 17.3, 8.7, 160),
|
||||
('大盘鸡', '西北菜', '鸡块110g土豆60g', 20.0, 11.9, 238),
|
||||
('孜然羊肉', '西北菜', '羊肉片90g洋葱40g', 18.4, 10.3, 182),
|
||||
('土豆炖牛肉', '西北菜', '牛肉80g土豆70g', 17.6, 5.5, 172),
|
||||
('糖醋里脊', '鲁菜', '里脊肉80g', 16.2, 10.0, 194),
|
||||
('酱爆鸡丁', '鲁菜', '鸡丁90g黄瓜30g', 17.3, 10.5, 211),
|
||||
('木须肉片', '鲁菜', '肉片50g鸡蛋25g木耳15g', 13.5, 9.2, 145),
|
||||
('红烧大排', '鲁菜', '猪大排1块', 8.8, 13.8, 198),
|
||||
('葱爆牛肉', '鲁菜', '牛肉片80g大葱50g', 16.9, 11.6, 184),
|
||||
],
|
||||
'veg': [
|
||||
('醋溜土豆丝', '家常菜', '土豆丝100g', 2.0, 3.2, 104),
|
||||
('香菇青菜', '家常菜', '香菇20g青菜80g', 1.8, 3.3, 44),
|
||||
('酸辣白菜', '家常菜', '白菜100g', 1.5, 3.1, 44),
|
||||
('清炒莴笋', '家常菜', '莴笋100g', 1.0, 5.1, 60),
|
||||
('手撕包菜', '家常菜', '包菜100g', 1.5, 3.2, 51),
|
||||
('韭菜炒蛋', '家常菜', '韭菜80g鸡蛋20g', 4.6, 7.1, 95),
|
||||
('西红柿炒蛋', '家常菜', '西红柿80g鸡蛋20g', 3.4, 6.9, 90),
|
||||
('蒜泥茼蒿菜', '家常菜', '茼蒿菜100g', 1.9, 3.3, 51),
|
||||
('炒什锦', '家常菜', '莴笋60g油面筋10g胡萝卜10g肉片15g', 5.4, 8.0, 106),
|
||||
('蒜泥杭白菜', '家常菜', '杭白菜100g', 1.8, 3.4, 45),
|
||||
('虾皮西葫芦', '家常菜', '西葫芦100g虾皮5g', 2.3, 3.3, 54),
|
||||
('彩椒炒鸡蛋', '家常菜', '彩椒80g鸡蛋20g', 3.5, 6.9, 91),
|
||||
('麻婆豆腐', '川菜', '豆腐100g肉末15g', 10.0, 8.5, 135),
|
||||
('鱼香茄子', '川菜', '茄子100g肉末15g', 3.8, 7.0, 130),
|
||||
('干煸四季豆', '川菜', '四季豆100g肉末10g', 3.8, 6.6, 94),
|
||||
('虎皮青椒', '川菜', '青椒100g', 1.0, 3.2, 49),
|
||||
('凉拌木耳', '川菜', '木耳50g黄瓜50g', 1.1, 2.2, 40),
|
||||
('酸辣藕丁', '川菜', '莲藕100g', 1.9, 3.2, 100),
|
||||
('红烧素鸡', '本帮菜', '汉康素鸡1块', 8.2, 11.2, 170),
|
||||
('四喜烤麸', '本帮菜', '烤麸60g香菇15g木耳15g花生10g', 10.0, 9.9, 170),
|
||||
('油焖茭白', '本帮菜', '茭白100g', 1.2, 5.2, 106),
|
||||
('葱油蚕豆', '本帮菜', '蚕豆100g', 8.8, 3.4, 131),
|
||||
('蒜泥西兰花', '江浙菜', '西兰花100g', 4.1, 3.6, 63),
|
||||
('清炒芦笋', '江浙菜', '芦笋100g', 2.6, 5.2, 67),
|
||||
('荷兰豆炒藕片', '江浙菜', '荷兰豆50g藕片50g', 2.2, 5.2, 96),
|
||||
('糖拌西红柿', '江浙菜', '西红柿150g', 1.4, 0.5, 75),
|
||||
('剁椒蒸豆腐', '湘菜', '豆腐100g剁椒10g', 8.3, 5.8, 103),
|
||||
('湘味藕片', '湘菜', '莲藕100g', 1.9, 3.2, 100),
|
||||
('白灼菜心', '粤菜', '菜心100g', 2.8, 2.5, 40),
|
||||
('蚝油生菜', '粤菜', '生菜100g', 1.3, 3.3, 42),
|
||||
('蒜蓉空心菜', '粤菜', '空心菜100g', 2.2, 2.3, 38),
|
||||
('上汤娃娃菜', '粤菜', '娃娃菜100g皮蛋10g', 3.1, 3.4, 50),
|
||||
('虾皮冬瓜', '粤菜', '冬瓜100g虾皮5g', 1.9, 3.3, 47),
|
||||
('韭菜炒豆芽', '西北菜', '豆芽80g韭菜20g', 2.2, 5.2, 65),
|
||||
('番茄炒包菜', '西北菜', '包菜70g番茄30g', 1.3, 5.2, 68),
|
||||
('蒜香茄子', '西北菜', '茄子100g', 1.1, 5.2, 68),
|
||||
('红枣南瓜', '面点小吃', '南瓜100g红枣5g', 1.0, 3.1, 64),
|
||||
('蛋黄焗南瓜', '面点小吃', '南瓜100g咸蛋黄15g', 3.1, 9.6, 118),
|
||||
('拔丝红薯', '面点小吃', '红薯100g', 1.1, 5.2, 170),
|
||||
('地三鲜', '鲁菜', '土豆50g茄子50g青椒20g', 1.8, 8.2, 126),
|
||||
('醋溜白菜', '鲁菜', '白菜100g', 1.5, 3.1, 44),
|
||||
('蒜蓉菠菜', '鲁菜', '菠菜100g', 2.6, 2.3, 46),
|
||||
('家常豆腐', '鲁菜', '豆腐100g青椒20g', 8.3, 6.7, 112),
|
||||
('清炒山药', '鲁菜', '山药100g木耳10g', 2.0, 5.2, 105),
|
||||
],
|
||||
'soup': [
|
||||
('番茄蛋汤', '家常菜', '西红柿20g鸡蛋10g', 1.5, 2.9, 36),
|
||||
('冬瓜排骨汤', '家常菜', '冬瓜25g排骨15g', 2.0, 4.2, 49),
|
||||
('大白菜贡丸汤', '家常菜', '大白菜20g贡丸20g', 2.7, 4.4, 61),
|
||||
('小排玉米棒胡萝卜汤', '家常菜', '小排20g玉米棒15g胡萝卜5g', 3.2, 5.0, 74),
|
||||
('紫菜虾皮蛋汤', '家常菜', '紫菜5g虾皮1g鸡蛋10g', 3.0, 3.0, 46),
|
||||
('汤骨海带丝汤', '家常菜', '汤骨15g海带丝15g', 2.4, 4.3, 53),
|
||||
('罗宋汤', '家常菜', '卷心菜15g土豆20g西红柿10g红肠5g', 1.5, 2.8, 50),
|
||||
('番茄菌菇汤', '家常菜', '西红柿20g菌菇20g', 1.5, 2.1, 30),
|
||||
('丝瓜蛋汤', '家常菜', '丝瓜25g鸡蛋10g', 1.6, 2.9, 37),
|
||||
('山药排骨汤', '江浙菜', '山药25g排骨15g', 2.4, 4.2, 60),
|
||||
('莲藕排骨汤', '江浙菜', '莲藕25g排骨15g', 2.4, 4.2, 64),
|
||||
('鲫鱼豆腐汤', '江浙菜', '鲫鱼20g豆腐15g', 4.6, 3.1, 52),
|
||||
],
|
||||
'staple': [
|
||||
('米饭', '面点小吃', '米饭150g', 3.9, 0.5, 174),
|
||||
('黑米饭', '面点小吃', '黑米饭150g', 4.8, 0.9, 183),
|
||||
('麦片饭', '面点小吃', '麦片饭150g', 6.0, 1.8, 195),
|
||||
('炒面', '面点小吃', '炒面150g', 6.8, 9.8, 262),
|
||||
('豆沙包', '面点小吃', '豆沙包30g', 2.5, 0.9, 70),
|
||||
('杂粮饭', '面点小吃', '杂粮饭150g', 5.2, 1.2, 188),
|
||||
('奶黄包', '面点小吃', '奶黄包30g', 2.1, 1.2, 70),
|
||||
('小笼包', '面点小吃', '小笼包3只', 6.8, 3.8, 165),
|
||||
('花卷', '面点小吃', '花卷50g', 3.5, 0.5, 110),
|
||||
('黄金馒头', '面点小吃', '馒头50g', 3.5, 0.5, 110),
|
||||
],
|
||||
}
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '写入示例菜品数据(含菜系)'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
count = 0
|
||||
for dish_type, items in SAMPLE_DISHES.items():
|
||||
for name, cuisine, ingredient, protein, fat, calorie in items:
|
||||
_, created = Dish.objects.update_or_create(
|
||||
name=name,
|
||||
defaults={
|
||||
'dish_type': dish_type,
|
||||
'cuisine': cuisine,
|
||||
'ingredient_detail': ingredient,
|
||||
'protein': protein,
|
||||
'fat': fat,
|
||||
'calorie': calorie,
|
||||
},
|
||||
)
|
||||
if created:
|
||||
count += 1
|
||||
self.stdout.write(self.style.SUCCESS(f'示例菜品已就绪(新增 {count} 道,库中共 {Dish.objects.count()} 道)'))
|
||||
Reference in New Issue
Block a user