项目在开发过程中经常会遇到与数据库架构相关的变更。有时还需要对现有数据进行修补。Frappe 提供了迁移和补丁系统工具来处理这些情况。
当你的应用中有架构变更时,要将现有站点的数据库迁移到新架构,你应该运行以下命令。
bench --site [sitename] migrate
架构变更
你可以编辑 DocType 来添加、删除或修改字段。保存 DocType 时,包含 DocType 数据的 JSON 文件会被添加到应用的源代码树中。当你将应用添加到站点时,会使用这个 JSON 文件来安装 DocTypes(创建数据库表)。
要进行架构变更,你必须启用开发者模式。
运行 migrate 时,所有应用都会迁移到其当前版本。首先,我们运行 “before_migrate” 钩子来同步用户权限。然后运行实际的补丁,迁移各个应用。之后,我们同步以下组件:
- 数据库架构
- 后台任务
- Fixtures(了解更多)
- 仪表板、桌面图标和网页
- 更新翻译
- 为所有路由重建搜索索引
特别是对于 DocTypes,我们会将每个 DocType JSON 的 MD5 哈希值与存储在 DocType 数据库表中的哈希值进行比较。如果哈希值不匹配,我们会重新加载该 DocType。这种技术称为校验和比较,你可以在这里了解更多。最后,我们运行 “after_migrate” 钩子来完成迁移。你可以在 bench-site-commands 中找到各个进程运行的命令。
注意:在
v13之前,DocTypes 是基于 JSON 文件中的修改时间戳来同步的。这种方法容易出错,所以我们改用了更可靠的哈希比较方法。你可以在这里了解更多。
当你删除或重命名 DocType 中的字段时,对应的数据库列不会从数据库表中删除,但它们不会在表单视图中显示。这样做是为了避免潜在的数据丢失情况,并允许你编写可能需要旧字段值的数据迁移(补丁)。
Frappe 不支持反向架构迁移。
数据迁移
在引入与数据相关的变更时,你可能需要运行一次性脚本来将现有数据更改为符合新代码预期的状态。在 Frappe 中,我们将这些脚本称为补丁。
编写补丁
要编写补丁,你必须在 Python 脚本中编写一个 execute 方法,并将其添加到应用的 patches.txt 中。
建议在文件路径中包含补丁编号和名称,并将其添加到应用的 patches 包(目录)中。然后你可以将补丁模块的点分路径添加到 patches.txt 中。
Frappe 中遵循的目录结构如下
frappe
└── patches
└── v12_0
└── my_awesome_patch.py
然后可以通过点分路径将补丁添加到 patches.txt 中。
frappe.patches.v12_0.my_awesome_patch
补丁执行期间的架构
execute 函数中可用的 DocType meta 将按照旧的 JSON 来提供。这样你就可以在编写迁移代码时假设仍然拥有旧字段。补丁运行完成后,新架构才会应用到 DocType。
如果你希望在补丁执行期间使用新架构,请使用 reload_doc 方法。
import frappe
def execute():
frappe.reload_doc(module_name, "doctype", doctype_name)
# your patch code here
模型同步后补丁 [仅限 v14 / develop 分支]
通常你的补丁可能不需要在 DocType 模型与数据库同步之前访问数据库架构。在这种情况下,最好将补丁放在 patches.txt 的 [post_model_sync] 部分中。
patches.txt 支持类似 INI 的文件格式,其中两个部分指定了补丁应该在何时运行——在 doctype 架构迁移之前或之后。模型同步后补丁不需要重新加载任何 doctypes,因为所有 doctypes 在执行它们之前都会被重新加载。以下是这种 patches.txt 文件的示例:
[pre_model_sync]
app.module.patch1
app.module.patch2
[post_model_sync]
app.module.patch3
app.module.patch4
一次性 Python 语句
你也可以使用以下语法在 patches.txt 中添加一次性 Python 语句,
frappe.patches.v12_0.my_awesome_patch
execute:frappe.delete_doc('Page', 'applications', ignore_missing=True)
补丁执行顺序
补丁按照定义的顺序运行。patches.txt 中的所有行必须是唯一的。如果某个补丁之前已经运行过,它就不会再次运行。如果你想再次运行某个补丁,可以添加一条注释,使该行看起来是新的。
例如,
frappe.patches.v12_0.my_awesome_patch #2019-09-08