Tandoor Recipes 全功能解析:开源菜谱管理、膳食规划与数据导入导出实战指南
【免费下载链接】recipesApplication for managing recipes, planning meals, building shopping lists and much much more!项目地址: https://gitcode.com/GitHub_Trending/re/recipes
导读
本文以仓库主页文档 docs/index.md 为骨架,系统梳理 Tandoor Recipes 这一开源菜谱管理系统的完整能力:从菜谱管理、膳食计划、购物清单等核心功能,到全文搜索、标签系统、批量导入等进阶特性,并深入展开其引用的导入导出体系(对应 docs/features/import_export.md)。读者读完将掌握 Tandoor 的部署形态、功能边界、数据迁移到 Tandoor 的完整操作流程,以及导入导出模块的源码级实现原理。
Tandoor Recipes 项目主页展示的应用界面预览,覆盖菜谱浏览、搜索与组织等核心场景。
一、Tandoor Recipes 是什么:项目定位与核心目标
Tandoor Recipes 是一款"管理不断增长的数字化菜谱收藏"的开源应用,其定位明确:面向拥有大量菜谱、希望与家人朋友分享或有序收藏的个人用户与家庭。项目主页同时强调了一个重要边界——应用带有基础权限系统,但不打算作为公开网站对外运行(见 docs/index.md)。
从仓库结构可以确认其技术栈为典型的现代 Web 应用:
- 后端:Django + Django REST Framework(入口配置见 recipes/settings.py、recipes/urls.py),核心业务模型集中在 cookbook/models.py;
- 前端:Vue 3 单页应用(源码位于 vue3/src),配合 Vuetify 组件库(vue3/src/vuetify.ts);
- 部署形态:官方推荐 Docker 容器化部署,仓库同时提供 Kubernetes、Unraid、Synology 等平台的示例与文档(见 docs/install 目录下的 docker.md、kubernetes.md、unraid.md、synology.md)。
项目主页将其定位概括为"由高级用户打造、为高级用户服务"(Made by and for power users),下文将逐项展开其核心能力。
二、核心功能:从菜谱到餐桌的一站式闭环
2.1 菜谱管理(Manage your recipes)
Tandoor 的核心是快速、直观的菜谱编辑器。从数据模型(cookbook/models.py)看,每条 Recipe 承载了完整的结构化信息:名称、描述、准备/等待时间、份数与份数文本、营养信息(按份或按菜谱计量)、步骤(Step)、食材(Ingredient)以及关键词标签(Keyword)等,足以支撑从菜谱录入到精细检索的全链路。
2.2 膳食计划(Plan multiple meals for each day)
应用支持每天规划多餐,MealPlan 数据模型将日期、膳食类型(MealType,如早餐/午餐/晚餐)与菜谱关联,为后续生成购物清单提供直接输入。
2.3 购物清单(Shopping lists)
购物清单存在两种生成路径:经由膳食计划自动汇总,或直接从菜谱添加。这一设计让"计划吃什么"与"去超市买什么"形成闭环。仓库中还扩展了超市(Supermarket)分组、食材忽略(ignore_shopping)等精细控制(相关模型与迁移可见 cookbook/models.py 与 cookbook/migrations 中 0075 之后的多次演进)。
2.4 食谱书(Cookbooks)
食谱书将散落的菜谱收集成书,RecipeBook 与 RecipeBookEntry 模型支持按顺序组织条目(cookbook/migrations/0239_add_ordering_property_recipebookentry.py),方便建立自己的"主题合集"。
2.5 分享与协作(Share and collaborate)
项目支持与家人朋友共享菜谱并协作:用户(User)、空间(Space)、邀请链接(InviteLink)等模型共同构成了多用户隔离与权限基础;同时,开发者明确提示项目"带基础权限系统,但不适合作为公共站点运行"。
2.6 更多实用特性(All the must haves)
主页还罗列了一组高频需求特性:
- 移动端优化:前端为移动设备做了适配,随时随地记录与查询;
- 多语言本地化:仓库 recipes/locale 与 cookbook/locale 下收录了 30+ 语言的翻译文件(
.po/.mo),并由社区持续维护; - 菜谱缩放:按份数自动换算食材用量;
- 图片压缩:图片处理集中在 cookbook/helper/image_processing.py,导入时自动处理;
- 打印视图:为纸质归档优化的打印页面;
- 超市管理:购物清单可按超市维度整理。
三、面向高级用户的深度能力(Made by and for power users)
3.1 强大且可定制的全文搜索(TrigramSimilarity)
Tandoor 的搜索基于 PostgreSQL 全文检索,并叠加TrigramSimilarity(三元组相似度)支持模糊匹配——即使拼写有偏差也能召回结果。搜索相关实现位于 cookbook/helper/recipe_search.py,配套的测试覆盖见 cookbook/tests/other/test_recipe_search_* 系列。
3.2 标签系统与批量操作
用户可以创建、搜索标签,并按过滤条件批量给所有匹配菜谱打标签。标签在数据模型中体现为树状结构 Keyword(相关迁移为 0147_keyword_to_tree,见 cookbook/migrations/0147_keyword_to_tree.py),导入的菜谱也会被自动归入对应的 "Import N" 标签树,方便追溯来源。
3.3 快速合并与重命名
针对食材、标签和单位,Tandoor 提供快速合并与重命名能力,帮助清理历史数据中的重复与不一致,是长期使用者的高频效率工具。
3.4 从网页批量导入菜谱
支持从成千上万个网站抓取菜谱,前提是页面提供 schema.org 的 Recipe,文档说明见 docs/features/external_recipes.md。
3.5 分数与小数双格式
应用同时支持**分数(fractions)或小数(decimals)**两种食材计量显示,用户可通过偏好设置切换(对应 UserPreference 中的 use_fractions 等字段)。
3.6 界面主题定制
通过主题(themes)机制自定义界面外观,仓库内置浅色/深色主题(cookbook/static/themes),可满足不同使用环境与视觉偏好。
3.7 云存储同步
支持与Dropbox 和 Nextcloud同步文件。同步层由 provider 抽象实现(cookbook/provider 目录下的 dropbox.py、nextcloud.py、local.py),并提供命令行同步入口(cookbook/management/commands/rebuildindex.py 所在的 management/commands 目录)。
3.8 云端 AI 能力(当前版本扩展)
除主页所述功能外,当前仓库 README(README.md)还列出了 AI 辅助能力:识别图片、整理菜谱步骤、查找营养信息等,相关模型与配置见 cookbook/migrations/0224_space_ai_credits_balance_space_ai_credits_monthly_and_more.py 等迁移文件与 cookbook/helper/ai_helper.py。
四、数据导入导出体系:数据自由进出的核心通道
主页将"从其他菜谱管理器导入收藏"列为高频需求,官方为此构建了以插件式集成(Integration)为核心的导入导出体系。这也是全项目中文档最翔实的模块之一(docs/features/import_export.md),下面做源码级展开。
4.1 架构:Integration 基类与统一分发
所有格式的导入导出都继承自同一个抽象基类Integration(cookbook/integration/integration.py)。基类定义了统一的职责边界:
- 导入侧:
get_recipe_from_file()将任意文件对象转换为 Recipe、split_recipe_file()将包含多条菜谱的文件拆分为列表; - 导出侧:
get_file_from_recipe()/get_files_from_recipes()将 Recipe 对象序列化为文件内容,并在多文件时打包为 ZIP; - 公共能力:自动创建 "Import N" 标签归类本次导入、基于菜谱名查重的
handle_duplicates()、图片处理import_recipe_image()等。
具体格式的分发由工厂函数get_integration(request, export_type)完成(cookbook/views/import_export.py),而支持的格式清单定义在表单基类ImportExportBase中(cookbook/forms.py)。仓库 cookbook/integration 目录下目前维护着 27 个具体集成实现,覆盖主流的菜谱管理与桌面/移动应用。
实现事实:虽然
ImportExportBase中仍保留PDF = 'PDF'常量以兼容数据库迁移,但get_integration()对 PDF 类型直接抛出NotImplementedError(cookbook/views/import_export.py),注释明确说明 pyppeteer 依赖已被移除——如需将菜谱存为 PDF,请使用浏览器自带的打印功能(Ctrl+P / Cmd+P)。
4.2 各集成能力总览
下表完整列出各集成当前的能力状态(✔️ = 已实现,❌ = 未实现且不计划/不可行,⌚ = 尚未实现):
| 集成 | 导入 | 导出 | 图片 |
|---|---|---|---|
| Default | ✔️ | ✔️ | ✔️ |
| Nextcloud | ✔️ | ⌚ | ✔️ |
| Mealie | ✔️ | ⌚ | ✔️ |
| Chowdown | ✔️ | ⌚ | ✔️ |
| Saffron | ✔️ | ✔️ | ❌ |
| Paprika | ✔️ | ⌚ | ✔️ |
| ChefTap | ✔️ | ❌ | ❌ |
| Pepperplate | ✔️ | ⌚ | ❌ |
| RecipeSage | ✔️ | ✔️ | ✔️ |
| Rezeptsuite.de | ✔️ | ❌ | ✔️ |
| Domestica | ✔️ | ⌚ | ✔️ |
| MealMaster | ✔️ | ❌ | ❌ |
| RezKonv | ✔️ | ❌ | ❌ |
| OpenEats | ✔️ | ❌ | ⌚ |
| Plantoeat | ✔️ | ❌ | ✔️ |
| CookBook Manager | ✔️ | ⌚ | ✔️ |
| Cooklang | ✔️ | ⌚ | ⌚ |
| CopyMeThat | ✔️ | ❌ | ✔️ |
| Mela | ✔️ | ⌚ | ✔️ |
| Cookmate | ✔️ | ⌚ | ✔️ |
| PDF (experimental) | ⌚️ | ✔️ | ✔️ |
| Gourmet | ✔️ | ❌ | ✔️ |
| Pestle | ✔️ | ❌ | ✔️ |
说明:官方明确采用"导入优先于导出"的策略——对大多数用户而言,"把既有收藏迁移进 Tandoor" 是第一优先级,因此各格式的导出能力(⌚)将随开发进度陆续补齐。WIP 提示:导入导出模块相对较新,大规模导出曾存在超时问题,正在修复中。
4.3 官方首选:Default 集成(Tandoor 原生格式)
Default 集成是内置且优先推荐的导入导出方式(实现见 cookbook/integration/default.py):
- 它随新字段持续维护,包含将菜谱从一套 Tandoor 安装迁移到另一套所需的全部数据;
- 其数据采用结构化 JSON,便于机器读取或用于其他用途;
- 导出时每条菜谱被封装为独立的
<recipeId>.zip(内含recipe.json与可选image.*),全部菜谱再打包为一个总 ZIP;导入端逐层解包并读取recipe.json。
重要约束:使用 Default 导入时,必须上传 Tandoor 导出的.zip归档文件。仅上传解压后的.json文件不被支持,可能导致导入错误;跨实例迁移请直接使用 ZIP 归档、不要先行解压。
从源码看,Default 导入通过RecipeExportSerializer(cookbook/serializer.py)完成反序列化与落库,该序列化器覆盖菜谱名称、描述、关键词、步骤、工作/等待时间、营养信息、份数与来源 URL 等核心字段。
4.4 各第三方格式导入实操指南
RecipeSage
在 RecipeSage 中进入Settings > Export Recipe Data,选择EXPORT AS JSON-LD (BEST),将导出文件直接上传到 Tandoor 导入。RecipeSage 集成同时支持导出:在 Tandoor 中选择 RecipeSage 类型导出,再把 json 文件导入 RecipeSage 即可实现反向迁移(注意:导出暂不支持图片)。
Domestica
在 Domestica 的Import/Export中选择Export Recipes,将导出的文件上传到 Tandoor。
Nextcloud Cookbook
Nextcloud Cookbook 提供规范、结构化的菜谱信息,导入后大多数字段可完整保留。操作步骤:
- 登录 Nextcloud Web 界面;
- 找到
Recipes文件夹(通常位于账号根目录); - 下载该文件夹,得到包含
Recipes目录的Recipes.zip,其中每个菜谱一个子目录; - 将
Recipes.zip上传到 Tandoor 导入。
目录结构要求(若未使用标准路径或自定义压缩方式,务必保证结构一致):
Recipes.zip/ └── Recipes/ ├── Recipe1/ │ ├── recipe.json │ └── full.jpg └── Recipe2/ ├── recipe.json └── full.jpgMealie
Mealie 提供与 Nextcloud 类似的结构化数据。迁移步骤:
- 进入 Mealie 管理后台创建备份(注意:仅从数据管理区导出菜谱数据会导致导入不完整);
- 下载备份文件;
- 将整个
.zip上传到 Tandoor 导入器。
需要注意两点:Mealie 存在两个版本导入器——1.0 之前的备份与1.0 及之后的备份分别对应不同的导入实现(cookbook/integration/mealie1.py 与 cookbook/integration/mealie.py);此外,Mealie 界面不会标明营养信息是按"每份"还是"每菜谱"存储,导入时需由用户指定 Tandoor 应如何处理营养数据。
Chowdown
Chowdown 将菜谱存储在纯文本 Markdown 文件中,目录名为_recipes,图片存放在images目录。将这两个目录打包为.zip后导入即可:
Recipes.zip/ ├── _recipes/ │ ├── recipe one.md │ ├── recipe two.md │ └── ... └── images/ ├── image-name.jpg ├── second-image-name.jpg └── ...兼容性细节:Chowdown 使用带下划线的
_recipes目录,为避免混淆,导入器同时支持_recipes与recipes两种写法。
Saffron
在 Saffron 设置页导出菜谱,将整个.zip上传到 Tandoor 即可。注意:Saffron 导出不含图片,图片将在导入过程中丢失。
Paprika
Paprika 的导出包含一个 HTML 表示的文件夹和一个.paprikarecipes文件——该文件本质上是 gzip 压缩的 ZIP。直接上传整个文件并导入全部菜谱即可。
Pepperplate
Pepperplate 提供包含全部菜谱.txt文件的.zip包,这些文件结构良好,可无损导入所有数据。从 Pepperplate 导出后上传 ZIP 即可;导出不含图片,因此图片无法导入。
ChefTap
ChefTap 的导出是包含cheftap_export文件夹的 ZIP,其中是菜谱.txt文件。由于该格式基本无结构化规范、每次导出形态各异,导入质量有限;通常导入器能识别出食材,其余内容归入步骤。图片不在导出内容中,因此不支持。由于 ChefTap 本身无法导入这类文件,Tandoor 也不会实现其导出器。
MealMaster
MealMaster 支持一次上传一个或多个文件,格式可为.txt、.MMF或.MM。目前仅支持单列格式的食材;食材的第二行注释不会被导入为注释,而是并入步骤文本。无法导入的 MealMaster 文件可提交 Issue 反馈。
RezKonv(RezKonv Suite)
该格式主要用于德国菜谱管理器 RezKonv Suite。迁移方式:在 RezKonv Suite 中选择Export > Gesamtes Kochbuch exportieren(导出菜单最后一项),将生成的文件直接导入 Tandoor。
Recipe Keeper
Recipe Keeper 可通过其应用导出包含菜谱与图片的 ZIP 文件,直接导入 Tandoor。
OpenEats
OpenEats 界面不提供导出功能,但可在命令行导出。在 OpenEats API 容器内执行:
python manage.py dumpdata recipe ingredient若采用默认安装方式,可运行:
docker-compose -f docker-prod.yml run --rm --entrypoint 'sh' api ./manage.py dumpdata recipe ingredient或:
docker exec -it openeats_api_1 ./manage.py dumpdata recipe ingredient rating recipe_groups > recipe_ingredients.json将输出的 JSON 字符串保存为.json文件并上传导入,格式大致如下:
[ { "model":"recipe.recipe", "pk":1, "fields":{ "title":"Tasty Chili", ... } }, ... { "model":"ingredient.ingredientgroup", "pk":1, "fields":{ "title":"Veges", "recipe":1 } }, ... { "model":"ingredient.ingredient", "pk":1, "fields":{ "title":"black pepper", "numerator":1.0, "denominator":1.0, "measurement":"dash", "ingredient_group":1 } } ]图片导入:在 Tandoor 的recipes媒体目录(通常为/opt/recipes/mediafiles)下创建openeats-import文件夹,将 OpenEats API 容器中/code/site-media/upload目录复制进去,最终应得到/opt/recipes/mediafiles/recipes/openeats-import/upload/...路径结构。
Plan to Eat
Plan to Eat 可导出包含全部菜谱的文本文件,直接上传该文本文件即可导入所有菜谱。
CookBook Manager(CookBookApp)
CookBook Manager 可导出包含 YAML 文件的.zip包:在Settings -> Backup中选择 YAML 选项导出,将整个 ZIP 上传到 Tandoor。
CopyMeThat
CopyMeThat 可导出包含一个.html文件与图片文件夹的.zip包,上传整个 ZIP 导入全部菜谱。
Cookmate
Cookmate 可导出.mcb文件,直接上传到 Tandoor 导入全部菜谱。
Cooklang
Cooklang 支持导入单个.cook文件(仓库配套测试见 cookbook/tests/other/test_cooklang_integration.py)。目前尚不支持附加图片或导入 ZIP 文件。
RecetteTek
RecetteTek 导出为.rtk文件,直接上传到 Tandoor 导入全部菜谱。
Rezeptsuite.de
Rezeptsuite.de 导出为.xml文件,直接上传导入。部分客户端可能导出包含.cml文件的.zip包——此时只需解压.zip并把.cml改名为.xml即可导入。
Mela
Mela 提供多种导出格式,但只有MelaRecipes格式能导出完整收藏。操作流程:执行该导出得到.melarecipes文件后,用解压工具(如 7-Zip)打开;若其中仍包含.melarecipes文件则继续解压,直到获得一个或多个.melarecipe文件;将需要导入的.melarecipe文件全部上传到 Tandoor 并开始导入。
基于 pyppeteer 的 PDF 导出已被移除。如需将菜谱保存为 PDF,请使用浏览器内置打印功能(Ctrl+P / Cmd+P)。
Gourmet
导入器针对 Gourmet(.grmt文件缺少食材单位的问题)设计为接收包含.htm与.jpg的.zip包:在 Gourmet 中导出为 HTML 并压缩生成文件夹。不支持导入菜单;由于.grmt格式存在问题,也不支持导出。
Pestle
从 Pestle 应用设置中点击 "Export",选择 "Pestle Recipe Format",得到mm-dd-yyyy.pestle文件用于导入。可导入的信息包括:名称、描述、份数、等待时间、工作时间、分类(关键词)、标签、食材(作为附加步骤)、步骤、营养、图片与来源。其他字段(评分、作者、备注、菜系、视频)将丢失。导出不支持,因为 Pestle 需要图片 URL,而导入数据中不包含。
4.5 ZIP 导入的安全边界与性能参数
由于多数格式以 ZIP 为载体,基类Integration(cookbook/integration/integration.py)内置了完整的解压安全防护,对应环境变量定义于 recipes/settings.py:
| 环境变量 | 默认值 | 作用 |
|---|---|---|
MAX_ZIP_FILE_COUNT | 2000 | ZIP 内最大文件数量 |
MAX_ZIP_TOTAL_SIZE | 500(MB) | 解压后总大小上限 |
MAX_ZIP_FILE_SIZE | 10(MB) | 单个文件解压后大小上限 |
MAX_ZIP_NESTING_DEPTH | 2 | 嵌套 ZIP 的最大层数 |
超过任一限制,导入器会中止并给出明确错误提示,有效防止"zip bomb"式恶意或误操作文件拖垮服务器。
4.6 命令行导入导出
除 Web 界面外,仓库还提供两个 Django management 命令:manage.py import与manage.py export(见 cookbook/management/commands/import.py 与 cookbook/management/commands/export.py)。二者分别继承 Django 内置的loaddata/dumpdata命令,并额外以scopes_disabled包装,从而绕过空间的django-scopes隔离限制,方便在脚本化迁移、备份恢复等场景下跨空间读写数据。
五、部署与运维要点
Tandoor 强调"使用 Docker 轻松搭建",仓库提供的部署资源包括:
- Docker Compose 示例:见 docs/install/docker.md,以及
docs/install下针对反向代理(nginx、traefik、apache)等场景的 compose 模板; - Kubernetes:完整 K8s 资源清单位于 docs/install/k8s(ConfigMap、Secret、PVC、StatefulSet、Deployment、Ingress 等);
- Unraid / Synology:分别提供安装指南(docs/install/unraid.md、docs/install/synology.md);
- Nginx 配置示例:仓库自带 nginx/conf.d/Recipes.conf 与模板文件 http.d/Recipes.conf.template。
典型部署依赖 PostgreSQL 数据库(全文检索与 TrigramSimilarity 依赖其能力),前端为 Vue 3 编译产物,由后端静态托管。生产环境建议按 docs/system/configuration.md 配置环境变量,其中上文提到的MAX_ZIP_*系列与EXPORT_FILE_CACHE_DURATION(默认 600 秒,控制导出文件的缓存时长,recipes/settings.py)均在此统一管理。
六、项目现状与路线图
主页文档(docs/index.md)坦诚地说明了项目所处阶段:应用在过去一年经历了快速迭代,引入了 Vue.js 等新技术,功能大量扩张的同时,部分细节与技术实现的品质仍有提升空间。因此,除持续更新的功能与里程碑(见仓库 Issues & Milestones)外,未来主要目标包括:
- 改善 UI:当前设计风格不一致,许多页面可用但观感欠佳;
- 拥抱开放数据与开放系统:为所有相关菜谱管理系统补齐导入器与导出器;
- 前端工程化清理:将所有 JavaScript 库迁移到包管理器统一管理,清理早期积累的技术债;
- 提升测试质量与覆盖率:改进既有测试并扩充用例;
- 完善文档:为全部功能与使用场景补齐文档,并增加应用内帮助。
项目最初的诞生动机是"索引、打标签和搜索个人收藏的数字化(PDF)菜谱",随着功能持续叠加,如今已成长为功能全面的菜谱管理系统。
结语
Tandoor Recipes 的价值在于把"菜谱收藏 → 膳食计划 → 购物清单 → 分享协作"这条生活化链路完整打通,同时以插件式集成架构(cookbook/integration)对 20 余种第三方格式提供导入能力,让用户的既有数据资产可以低成本迁移。对于希望深入了解实现细节的读者,推荐继续阅读:
- 导入导出全量文档:docs/features/import_export.md;
- 网页抓取导入文档:docs/features/external_recipes.md;
- 集成基类实现:cookbook/integration/integration.py;
- 原生格式序列化:cookbook/serializer.py(
RecipeExportSerializer); - 各平台部署指南:docs/install。
【免费下载链接】recipesApplication for managing recipes, planning meals, building shopping lists and much much more!项目地址: https://gitcode.com/GitHub_Trending/re/recipes
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考