> ## Content Index
> Fetch the complete content index at: https://blog.vercanti.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Aerich 迁移指南
- URL: https://blog.vercanti.com/aerich-qian-yi-zhi-nan/
- Published: 2026-08-28T14:34:50.000Z
- Updated: 2026-08-28T14:57:28.000Z
- Description: Aerich 是 Tortoise ORM 的官方数据库迁移工具，功能类似 Django Migrations 或 Alembic，支持生成迁移文件、升级、回滚。 运行后会生成 aerich.ini 文件和 migrations/ 目录。 使用 pyproject.toml 可以避免项目根目录多出 aerich.ini，与现代 Python 项目工具链保持一致。当 aerich.ini 和 pyproject.toml 同时存在时，aerich.ini 优先。 Aerich 要求配置中的 apps 里必须包含 aerich.models，用于存储迁移版本
- Author: yellowdog
- Tags: Tortoise-orm

> 官方文档：<https://github.com/tortoise/aerich>  
> 适用版本：aerich 0.7+，tortoise-orm 0.20+（2026-05-08 核实）  
> 最后更新：2026-04-11

---

## 一、安装与初始化

```bash
pip install aerich

```

Aerich 是 Tortoise ORM 的官方数据库迁移工具，功能类似 Django Migrations 或 Alembic，支持生成迁移文件、升级、回滚。

### 1.1 初始化项目

```bash
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`

```ini
[aerich]
tortoise_orm = myapp.config.TORTOISE_ORM
location = ./migrations
src_folder = ./.

```

| 字段            | 说明                            |
| ------------- | ----------------------------- |
| tortoise\_orm | TORTOISE\_ORM 配置的 Python 模块路径 |
| location      | 迁移文件目录                        |
| src\_folder   | 模块搜索根目录                       |

### 2.2 `pyproject.toml` 中配置（推荐）

```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`，用于存储迁移版本记录。

```python
# 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`

首次初始化数据库，创建所有表并生成初始迁移文件。只在项目初始化时运行一次。

```bash
aerich init-db

```

| 参数      | 类型   | 默认值   | 说明                                    |
| ------- | ---- | ----- | ------------------------------------- |
| \--safe | bool | False | 使用 CREATE TABLE IF NOT EXISTS，表已存在不报错 |

执行后会在 `migrations/{app_name}/` 下生成类似 `0_20260101000000_init.py` 的文件，并在数据库中创建 `aerich` 表用于记录版本。

### 4.2 `aerich migrate`

检测模型变化，生成新的迁移文件（不执行到数据库）。

```bash
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`

将所有未应用的迁移文件执行到数据库。

```bash
aerich upgrade

```

| 参数               | 类型  | 默认值  | 说明                  |
| ---------------- | --- | ---- | ------------------- |
| \--app           | str | None | 仅升级指定 app 的迁移       |
| \-v / \--version | int | None | 升级到指定版本号（不指定则升级到最新） |

### 4.4 `aerich downgrade`

回滚迁移到指定版本。

```bash
aerich downgrade -v 1

```

| 参数               | 类型   | 默认值   | 说明            |
| ---------------- | ---- | ----- | ------------- |
| \-v / \--version | int  | 无，必填  | 回滚到的目标版本号     |
| \-d / \--delete  | bool | False | 回滚后删除对应的迁移文件  |
| \--app           | str  | None  | 仅回滚指定 app 的迁移 |

### 4.5 `aerich history`

查看已应用的迁移历史记录。

```bash
aerich history

```

| 参数     | 类型  | 默认值  | 说明            |
| ------ | --- | ---- | ------------- |
| \--app | str | None | 仅查看指定 app 的历史 |

输出示例：

```
1_20260101000000_init
2_20260110120000_add_user_avatar

```

### 4.6 `aerich heads`

查看当前最新版本（尚未应用到数据库的迁移头）。

```bash
aerich heads

```

| 参数     | 类型  | 默认值  | 说明               |
| ------ | --- | ---- | ---------------- |
| \--app | str | None | 仅查看指定 app 的 head |

### 4.7 `aerich inspectdb`

从已有数据库逆向生成 Tortoise ORM 模型代码（适合接手旧项目）。

```bash
aerich inspectdb
aerich inspectdb --table users --table orders

```

| 参数       | 类型         | 默认值  | 说明               |
| -------- | ---------- | ---- | ---------------- |
| \--table | str（可多次指定） | None | 仅逆向指定表，不指定则逆向所有表 |

输出为标准输出，建议重定向到文件：

```bash
aerich inspectdb > myapp/models_generated.py

```

---

## 五、迁移文件结构

```python
# 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`：升级时执行的 SQL
- `downgrade`：回滚时执行的 SQL
- 每个函数返回 SQL 字符串（多条语句用分号分隔）或直接执行 `db.execute_script(sql)`

手写迁移时可以直接操作 `db` 对象：

```python
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` 中配置多个应用，每个应用有独立的迁移目录。

```python
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 运行迁移命令：

```bash
aerich migrate --app users --name add_avatar
aerich upgrade --app users
aerich downgrade --app orders -v 0

```

> 注意：`aerich.models` 只需要包含在一个 app 中，通常放在主 app 里。

---

## 七、FastAPI 集成示例

```python
# 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 示例

```bash
#!/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
# 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 标准部署步骤

```bash
# 1. 拉取最新代码
git pull origin main

# 2. 安装依赖
pip install -r requirements.txt

# 3. 执行迁移（生产环境一定要先备份数据库）
aerich upgrade

# 4. 重启服务
systemctl restart myapp

```

### 8.3 CI/CD 集成（GitHub Actions 示例）

```yaml
- name: Run migrations
  env:
    DATABASE_URL: ${{ secrets.DATABASE_URL }}
  run: |
    aerich upgrade

```

---

## 九、踩坑与注意事项

### 9.1 迁移文件冲突

**场景**：多人协作时，两个开发者同时基于同一版本生成了迁移文件，版本号相同。

**现象**：`aerich upgrade` 时报错，提示版本冲突。

**解决**：手动将其中一个迁移文件的版本号调高，并修改文件名中的版本序号，确保迁移链不断。

```bash
# 查看当前 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()` 函数可以返回空字符串并加注释说明
- 建议在测试环境演练回滚流程

```python
async def downgrade(db: BaseDBAsyncClient) -> str:
    # 此迁移不可逆（数据迁移），回滚前请手动恢复备份
    return ""

```

### 9.3 字段重命名需手动处理

Aerich 的自动检测基于字段名比对，无法区分"删除旧字段 + 新增新字段"和"重命名字段"。

**错误做法**：直接修改模型中的字段名，然后 `aerich migrate`，会生成先删除再新增的操作，导致数据丢失。

**正确做法**：生成空迁移后手动编写 SQL。

```bash
aerich migrate --name rename_username_to_name --empty

```

```python
# 手动编写迁移内容
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` 数据库中。更多多数据库配置见 [多数据库路由](https://blog.vercanti.com/duo-shu-ju-ku-lu-you/)。

---

## 最佳实践

**始终用 `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>`。

---

## 参见

[初始化与配置](https://blog.vercanti.com/tortoise-orm-chu-shi-hua-yu-pei-zhi/)  
[多数据库路由](https://blog.vercanti.com/duo-shu-ju-ku-lu-you/)  
[模型字段完全参考](https://blog.vercanti.com/tortoise-orm-mo-xing-zi-duan-wan-quan-can-kao/)