Aerich 迁移指南
Aerich 是 Tortoise ORM 的官方数据库迁移工具,功能类似 Django Migrations 或 Alembic,支持生成迁移文件、升级、回滚。 运行后会生成 aerich.ini 文件和 migrations/ 目录。 使用 pyproject.toml 可以避免项目根目录多出 aerich.ini,与现代 Python 项目工具链保持一致。当 aerich.ini 和 pyproject.toml 同时存在时,aerich.ini 优先。 Aerich 要求配置中的 apps 里必须包含 aerich.models,用于存储迁移版本
官方文档:https://github.com/tortoise/aerich
适用版本:aerich 0.7+,tortoise-orm 0.20+(2026-05-08 核实)
最后更新:2026-04-11
一、安装与初始化
pip install aerich
Aerich 是 Tortoise ORM 的官方数据库迁移工具,功能类似 Django Migrations 或 Alembic,支持生成迁移文件、升级、回滚。
1.1 初始化项目
aerich init -t myapp.config.TORTOISE_ORM
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-t / --tortoise-orm |
str |
无,必填 | 指向 TORTOISE_ORM 配置字典的 Python 路径 |
--location |
str |
./migrations |
迁移文件存放目录 |
-s / --src-folder |
str |
./. |
源码根目录(用于模块导入) |
运行后会生成 aerich.ini 文件和 migrations/ 目录。
二、配置文件
2.1 aerich.ini
[aerich]
tortoise_orm = myapp.config.TORTOISE_ORM
location = ./migrations
src_folder = ./.
| 字段 | 说明 |
|---|---|
tortoise_orm |
TORTOISE_ORM 配置的 Python 模块路径 |
location |
迁移文件目录 |
src_folder |
模块搜索根目录 |
2.2 pyproject.toml 中配置(推荐)
[tool.aerich]
tortoise_orm = "myapp.config.TORTOISE_ORM"
location = "./migrations"
src_folder = "./."
使用 pyproject.toml 可以避免项目根目录多出 aerich.ini,与现代 Python 项目工具链保持一致。当 aerich.ini 和 pyproject.toml 同时存在时,aerich.ini 优先。
三、TORTOISE_ORM 配置要求
Aerich 要求配置中的 apps 里必须包含 aerich.models,用于存储迁移版本记录。
# myapp/config.py
TORTOISE_ORM = {
"connections": {
"default": "postgres://user:pass@localhost:5432/mydb"
},
"apps": {
"models": {
"models": ["myapp.models", "aerich.models"], # aerich.models 必须包含
"default_connection": "default",
}
},
"use_tz": True,
"timezone": "Asia/Shanghai",
}
四、迁移命令详解
4.1 aerich init-db
首次初始化数据库,创建所有表并生成初始迁移文件。只在项目初始化时运行一次。
aerich init-db
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--safe |
bool |
False |
使用 CREATE TABLE IF NOT EXISTS,表已存在不报错 |
执行后会在 migrations/{app_name}/ 下生成类似 0_20260101000000_init.py 的文件,并在数据库中创建 aerich 表用于记录版本。
4.2 aerich migrate
检测模型变化,生成新的迁移文件(不执行到数据库)。
aerich migrate --name add_user_avatar
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--name |
str |
update |
迁移文件名称后缀,建议使用描述性名称 |
--empty |
bool |
False |
生成空迁移文件,用于手写自定义 SQL |
--app |
str |
None |
仅对指定 app 生成迁移(多 app 项目使用) |
生成文件命名规则:{version}_{timestamp}_{name}.py,例如 1_20260101120000_add_user_avatar.py。
4.3 aerich upgrade
将所有未应用的迁移文件执行到数据库。
aerich upgrade
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--app |
str |
None |
仅升级指定 app 的迁移 |
-v / --version |
int |
None |
升级到指定版本号(不指定则升级到最新) |
4.4 aerich downgrade
回滚迁移到指定版本。
aerich downgrade -v 1
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-v / --version |
int |
无,必填 | 回滚到的目标版本号 |
-d / --delete |
bool |
False |
回滚后删除对应的迁移文件 |
--app |
str |
None |
仅回滚指定 app 的迁移 |
4.5 aerich history
查看已应用的迁移历史记录。
aerich history
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--app |
str |
None |
仅查看指定 app 的历史 |
输出示例:
1_20260101000000_init
2_20260110120000_add_user_avatar
4.6 aerich heads
查看当前最新版本(尚未应用到数据库的迁移头)。
aerich heads
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--app |
str |
None |
仅查看指定 app 的 head |
4.7 aerich inspectdb
从已有数据库逆向生成 Tortoise ORM 模型代码(适合接手旧项目)。
aerich inspectdb
aerich inspectdb --table users --table orders
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--table |
str(可多次指定) |
None |
仅逆向指定表,不指定则逆向所有表 |
输出为标准输出,建议重定向到文件:
aerich inspectdb > myapp/models_generated.py
五、迁移文件结构
# migrations/models/1_20260101000000_init.py
from tortoise import BaseDBAsyncClient
async def upgrade(db: BaseDBAsyncClient) -> str:
return """
CREATE TABLE IF NOT EXISTS "user" (
"id" SERIAL NOT NULL PRIMARY KEY,
"username" VARCHAR(50) NOT NULL UNIQUE,
"email" VARCHAR(255) NOT NULL,
"created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
"""
async def downgrade(db: BaseDBAsyncClient) -> str:
return """
DROP TABLE IF EXISTS "user";
"""
upgrade:升级时执行的 SQLdowngrade:回滚时执行的 SQL- 每个函数返回 SQL 字符串(多条语句用分号分隔)或直接执行
db.execute_script(sql)
手写迁移时可以直接操作 db 对象:
async def upgrade(db: BaseDBAsyncClient) -> str:
await db.execute_script("""
UPDATE "user" SET email = LOWER(email);
ALTER TABLE "user" ADD COLUMN "avatar_url" VARCHAR(500);
""")
return ""
六、多应用(Multi-App)迁移配置
当项目有多个独立模块时,可以在 apps 中配置多个应用,每个应用有独立的迁移目录。
TORTOISE_ORM = {
"connections": {
"default": "postgres://user:pass@localhost:5432/mydb"
},
"apps": {
"users": {
"models": ["myapp.users.models", "aerich.models"],
"default_connection": "default",
},
"orders": {
"models": ["myapp.orders.models"],
"default_connection": "default",
},
},
}
迁移目录结构:
migrations/
users/
0_20260101000000_init.py
1_20260110000000_add_avatar.py
orders/
0_20260101000000_init.py
对指定 app 运行迁移命令:
aerich migrate --app users --name add_avatar
aerich upgrade --app users
aerich downgrade --app orders -v 0
注意:
aerich.models只需要包含在一个 app 中,通常放在主 app 里。
七、FastAPI 集成示例
# main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from tortoise import Tortoise
from myapp.config import TORTOISE_ORM
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动时初始化数据库连接
# 迁移由 aerich upgrade 在部署时执行,不在这里执行
await Tortoise.init(config=TORTOISE_ORM)
yield
# 关闭时释放连接
await Tortoise.close_connections()
app = FastAPI(lifespan=lifespan)
在 FastAPI 中,lifespan 只负责初始化连接,不负责执行迁移。迁移应在部署流程中单独执行。
八、生产环境部署流程
8.1 Docker entrypoint 示例
#!/bin/bash
# entrypoint.sh
set -e
echo "Running database migrations..."
aerich upgrade
echo "Starting application..."
exec uvicorn myapp.main:app --host 0.0.0.0 --port 8000
# Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
8.2 标准部署步骤
# 1. 拉取最新代码
git pull origin main
# 2. 安装依赖
pip install -r requirements.txt
# 3. 执行迁移(生产环境一定要先备份数据库)
aerich upgrade
# 4. 重启服务
systemctl restart myapp
8.3 CI/CD 集成(GitHub Actions 示例)
- name: Run migrations
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: |
aerich upgrade
九、踩坑与注意事项
9.1 迁移文件冲突
场景:多人协作时,两个开发者同时基于同一版本生成了迁移文件,版本号相同。
现象:aerich upgrade 时报错,提示版本冲突。
解决:手动将其中一个迁移文件的版本号调高,并修改文件名中的版本序号,确保迁移链不断。
# 查看当前 head
aerich heads
# 重命名冲突文件,修改版本号
mv migrations/models/2_xxx_feature_b.py migrations/models/3_xxx_feature_b.py
# 同时修改文件内部的版本引用(如有 depends_on 字段)
9.2 downgrade 数据丢失风险
downgrade 会执行迁移文件的 downgrade() 函数,通常包含 DROP COLUMN、DROP TABLE 等破坏性操作。
- 生产环境回滚前务必先备份数据库
- 对于不可逆的迁移(如数据转换),
downgrade()函数可以返回空字符串并加注释说明 - 建议在测试环境演练回滚流程
async def downgrade(db: BaseDBAsyncClient) -> str:
# 此迁移不可逆(数据迁移),回滚前请手动恢复备份
return ""
9.3 字段重命名需手动处理
Aerich 的自动检测基于字段名比对,无法区分"删除旧字段 + 新增新字段"和"重命名字段"。
错误做法:直接修改模型中的字段名,然后 aerich migrate,会生成先删除再新增的操作,导致数据丢失。
正确做法:生成空迁移后手动编写 SQL。
aerich migrate --name rename_username_to_name --empty
# 手动编写迁移内容
async def upgrade(db: BaseDBAsyncClient) -> str:
return """
ALTER TABLE "user" RENAME COLUMN "username" TO "name";
"""
async def downgrade(db: BaseDBAsyncClient) -> str:
return """
ALTER TABLE "user" RENAME COLUMN "name" TO "username";
"""
9.4 aerich.models 缺失报错
现象:运行 aerich init-db 或 aerich migrate 时报错 Table aerich does not exist。
原因:TORTOISE_ORM 的 models 列表中没有包含 aerich.models。
解决:在至少一个 app 的 models 列表中加入 "aerich.models"。
9.5 多数据库环境下的迁移
每个数据库连接的迁移需要独立管理,使用 --app 参数指定。Aerich 的迁移版本记录(aerich 表)存储在对应 app 的 default_connection 数据库中。更多多数据库配置见 多数据库路由。
最佳实践
始终用 aerich migrate 生成迁移文件,不要手动修改数据库:手动 ALTER TABLE 与 Aerich 迁移历史不同步,会导致后续迁移冲突甚至回滚失败。
迁移文件纳入版本控制:migrations/ 目录必须提交到 Git,团队协作时每人拉取后运行 aerich upgrade 保持数据库同步,而非各自独立迁移。
生产环境迁移前备份数据库:aerich upgrade 执行 DDL 语句,若迁移失败可能导致部分修改无法自动回滚(MySQL 的 DDL 不在事务中),迁移前手动备份是最后防线。
测试环境使用 generate_schemas=True,生产使用迁移:Tortoise.init(generate_schemas=True) 适合测试中快速建表,生产环境必须用 Aerich 迁移管理,保证升降级路径清晰。
重命名字段用两步迁移:先添加新字段并迁移数据,再删除旧字段,而非直接重命名(Aerich autogenerate 无法区分重命名和删除+新增,直接生成的迁移会丢数据)。
常见陷阱
陷阱:aerich init-db 后数据库已有表,再 aerich migrate 冲突
现象: 新项目用 generate_schemas 建表后才引入 Aerich,aerich init-db 报表已存在。
原因: init-db 会执行 CREATE TABLE,但表已由 generate_schemas 创建。
解决: 在空数据库上运行 init-db;若表已存在,手动插入 aerich 版本记录(0001 → 初始状态)跳过 init,或删表重建。
陷阱:model 变更后未运行 aerich migrate 导致列不存在
现象: 代码中新增 model 字段后应用启动报 Unknown column 'xxx' in 'field list'。
原因: Model 类已更新但数据库结构没有同步,缺少 aerich migrate && aerich upgrade 步骤。
解决: 建立开发流程:修改 Model → aerich migrate -n <desc> → 审查迁移文件 → aerich upgrade → 提交代码+迁移文件。
陷阱:多 app 时未指定 --app 导致迁移到错误数据库
现象: 在多数据库场景下,aerich migrate 将迁移应用到了错误的数据库。
原因: 未指定 --app 时 Aerich 默认操作第一个 app,多 app 环境需显式指定。
解决: 多 app 场景始终用 aerich migrate --app <app_name> 和 aerich upgrade --app <app_name>。