跳到主要内容

FastAPI项目数据库迁移工具Alembic

在 FastAPI 项目中,通常会搭配 SQLAlchemy 作为 ORM。如果你的数据库已经存在且有数据,当修改了 models.py 中的表结构后,绝对不能直接删表重建,也不能仅靠 SQLAlchemy 的 Base.metadata.create_all()(因为它只会创建不存在的表,不会修改已存在的表结构)。

业界标准的做法是使用 Alembic 这个数据库迁移工具。

以下是针对你的情况(已有数据库,需要更新表结构)的完整处理流程。分为两种情况:


情况一:你的项目之前还没用过 Alembic

如果你的数据库最初是通过 Base.metadata.create_all(bind=engine) 直接生成的,现在需要引入 Alembic 来管理后续的修改,请严格按照以下步骤操作(核心是 stamp 步骤):

1. 安装 Alembic

在你的 FastAPI 虚拟环境中安装:

pip install alembic

2. 初始化 Alembic

在项目根目录下运行:

alembic init alembic

这会生成一个 alembic.ini 配置文件和一个 alembic/ 目录。

3. 配置 Alembic

第一步:修改 alembic.ini 找到 sqlalchemy.url,将其修改为你的数据库连接字符串(或者你可以留空,在 env.py 中动态读取 FastAPI 的配置)。

# alembic.ini
sqlalchemy.url = mysql+pymysql://user:password@localhost/dbname

第二步:修改 alembic/env.py 让 Alembic 知道你的数据库模型长什么样。找到 target_metadata = None 并修改为:

# alembic/env.py
import sys
import os
# 将项目根目录加入 sys.path,确保能导入你的 models
sys.path.append(os.path.dirname(os.path.dirname(__file__)))

# 导入你的 SQLAlchemy Base 和所有的 models
# 注意:必须导入所有定义了表的 models,否则 Alembic 识别不到
from database import Base
import models

# 修改 target_metadata
target_metadata = Base.metadata

4. 生成初始迁移脚本(关键)

此时你的 models.py 应该和当前数据库的实际结构保持一致。运行以下命令生成初始的迁移脚本:

alembic revision --autogenerate -m "init existing db"

5. 标记数据库状态(Stamp - 极其重要!)

因为你的数据库表已经存在了,如果你直接运行 upgrade,Alembic 会尝试重新建表并报错。 你需要告诉 Alembic:“数据库现在已经是这个初始状态了,把当前版本号写进数据库,但不要执行建表 SQL”。

alembic stamp head

(执行完后,数据库里会多出一张 alembic_version 表,里面记录了当前的迁移版本号。)

6. 修改 Models 并更新数据库

现在你可以修改你的 models.py 了(比如增加一个字段)。修改完后:

  1. 生成新的迁移脚本
    alembic revision --autogenerate -m "add new column to user table"
  2. 检查生成的脚本:打开 alembic/versions/ 下新生成的 .py 文件,确认 upgrade() 函数里的 add_column 等操作是否符合预期。
  3. 应用更新到数据库
    alembic upgrade head

情况二:你的项目已经配置好了 Alembic

如果你之前已经在使用 Alembic 管理数据库了,那么每次修改 models.py 后,只需要简单两步:

  1. 自动生成迁移脚本

    alembic revision --autogenerate -m "描述你的修改,例如:add email column"

    Alembic 会自动对比 models.py 和当前数据库结构的差异,并生成对应的 ALTER TABLE 脚本。

  2. 执行迁移更新数据库

    alembic upgrade head

💡 最佳实践与避坑指南

  1. 不要完全信任 --autogenerate:Alembic 的自动生成能识别新增字段、删除字段、新增表,但很难准确识别表名修改或字段名修改(它可能会识别为“删除旧字段+新增新字段”,导致数据丢失)。每次生成迁移脚本后,一定要打开脚本文件人工检查一下 upgrade()downgrade() 函数
  2. 生产环境更新:在生产环境更新时,通常是先在本地开发环境生成好 Alembic 迁移脚本,将脚本提交到 Git。部署到生产服务器后,直接在服务器上运行 alembic upgrade head 即可安全更新表结构。
  3. 废弃 create_all:引入 Alembic 后,建议把代码中启动时的 Base.metadata.create_all(bind=engine) 删掉或注释掉,统一交由 Alembic 来管理数据库的创建和修改。

是的,你的理解非常准确!生成迁移脚本、生成初始脚本以及标记数据库状态(Stamp),通常都是在“本地开发环境”中完成的。

为了让你更清晰地理解整个生命周期,我们可以把流程分为 “本地开发”“线上生产(或测试环境)” 两个阶段来看:

阶段一:在“本地开发环境”引入 Alembic(首次操作)

假设你的本地数据库和线上数据库都已经有数据和表结构了:

  1. 生成初始迁移脚本:在本地运行 alembic revision --autogenerate -m "init existing db"
    • 目的:Alembic 会读取你本地的 models.py,生成一个包含所有建表语句的 .py 脚本。
  2. 本地 Stamp(标记状态):在本地运行 alembic stamp head
    • 目的:告诉本地数据库:“你已经有这些表了,直接把刚才生成的脚本版本号记录到 alembic_version 表里,不要去执行建表语句。”
  3. 提交代码:将 alembic.inialembic/ 目录以及里面生成的 versions/xxxx_init_existing_db.py 脚本全部提交到 Git

阶段二:将 Alembic 同步到“生产/测试环境”(首次操作)

当你的代码部署到服务器(线上或测试环境)时,服务器的数据库也是已经存在表结构的:

  1. 拉取代码:服务器拉取你刚刚提交的包含 Alembic 脚本的代码。
  2. 服务器 Stamp(极其重要):在服务器上运行 alembic stamp head
    • 目的:服务器的数据库原本也没有 Alembic 的版本记录。这一步是告诉生产库:“你也已经处于初始状态了,直接把版本号写进 alembic_version,不要重新建表。”
    • 注意:在服务器上绝对不要运行 --autogenerate,你只需要复用本地生成的那个初始脚本即可。

阶段三:后续的日常开发(修改表结构)

一旦初始状态(Stamp)在所有环境都对齐了,以后的流程就非常标准化了:

  1. 本地修改:在本地修改 models.py(比如加个字段)。
  2. 本地生成脚本:在本地运行 alembic revision --autogenerate -m "add new column"
  3. 本地更新:在本地运行 alembic upgrade head,测试修改是否成功。
  4. 提交代码:把新生成的 versions/yyyy_add_new_column.py 提交到 Git。
  5. 服务器更新:代码部署到服务器后,直接在服务器运行 alembic upgrade head。此时服务器会读取新脚本,执行 ALTER TABLE 语句,安全地更新线上表结构。

总结

  • --autogenerate(生成脚本)永远只在本地开发环境执行。脚本生成后应该作为代码的一部分提交到版本控制系统(Git)。
  • stamp head(标记状态):在引入 Alembic 的初期,每个已经存在表结构的数据库(本地库、测试库、生产库)都需要执行一次,以建立基准线。
  • upgrade head(执行迁移):在日常开发中,本地测试时执行,生产环境部署时执行。