使用Alembic进行数据迁移

大型 FastAPI 项目 中,数据库迁移(Database Migration)功能通常使用 Alembic 进行管理,并且存放在 migrations/ 目录下。

1. 数据库迁移的目录结构

在 FastAPI 项目中,数据库迁移的目录结构如下:

my_fastapi_project/
│── app/                      # 应用主目录
│   ├── core/                 # 核心配置
│   │   ├── database.py       # 数据库连接
│   │   ├── config.py         # 配置管理
│   │   ├── __init__.py
│── migrations/               # Alembic 迁移管理
│   ├── versions/             # 存放迁移版本文件
│   ├── env.py                # Alembic 配置文件
│   ├── script.py.mako        # 迁移模板
│   ├── README                # 迁移说明
│   ├── alembic.ini           # Alembic 配置
│── .env                      # 环境变量
│── requirements.txt          # 依赖项
│── alembic.ini               # Alembic 配置
│── Dockerfile                # Docker 配置
│── docker-compose.yml        # Docker-Compose 配置
│── README.md                 # 项目说明

其中:

​ • migrations/:存放数据库迁移的所有内容。

​ • migrations/versions/:存放具体的迁移版本文件(XXXX_initial_migration.py)。

​ • migrations/env.py:Alembic 运行时的数据库环境配置。

​ • alembic.ini:Alembic 全局配置文件。

2. 配置 Alembic 进行数据库迁移

第一步:安装 Alembic

pip install alembic

第二步:初始化 Alembic

在项目根目录运行:

alembic init migrations

执行后,会在项目目录下生成 migrations/ 目录,结构如下:

migrations/
│── versions/                  # 迁移版本文件夹
│── env.py                     # Alembic 运行时配置
│── script.py.mako             # 迁移模板
│── README                     # 说明文件
│── alembic.ini                # Alembic 配置文件

3. 配置 env.py 连接数据库

打开 migrations/env.py,找到 config = context.config,修改如下:

from logging.config import fileConfig

from sqlalchemy import engine_from_config, create_engine
from sqlalchemy import pool

from alembic import context


# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config

# Interpret the config file for Python logging.
# This line sets up loggers basically.
if config.config_file_name is not None:
    fileConfig(config.config_file_name)

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
from app.models import *  # noqa: F401
# target_metadata = mymodel.Base.metadata
from app.models.base import Base  # noqa: F401
# target_metadata = None
target_metadata = Base.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
from app.core.config import settings  # noqa: F401
config.set_main_option("sqlalchemy.url", settings.MYSQL_DATABASE_URL)


def run_migrations_offline() -> None:
    """Run migrations in 'offline' mode.

    This configures the context with just a URL
    and not an Engine, though an Engine is acceptable
    here as well.  By skipping the Engine creation
    we don't even need a DBAPI to be available.

    Calls to context.execute() here emit the given string to the
    script output.

    """
    url = config.get_main_option("sqlalchemy.url")
    context.configure(
        url=url,
        target_metadata=target_metadata,
        literal_binds=True,
        dialect_opts={"paramstyle": "named"},
    )

    with context.begin_transaction():
        context.run_migrations()


def run_migrations_online() -> None:
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    # connectable = engine_from_config(
    #     config.get_section(config.config_ini_section, {}),
    #     prefix="sqlalchemy.",
    #     poolclass=pool.NullPool,
    # )
    engine = create_engine(settings.MYSQL_DATABASE_URL)
    with engine.connect() as connection:
        context.configure(
            connection=connection, target_metadata=target_metadata
        )
        with context.begin_transaction():
            context.run_migrations()


if context.is_offline_mode():
    run_migrations_offline()
else:
    run_migrations_online()

⚠️ 注意

​ • 需要导入 Base.metadata,否则 Alembic 无法识别你的数据模型。

​ • DATABASE_URL 从 app/core/config.py 读取,保证与应用使用的数据库一致。

4. 创建数据库迁移

当你新增或修改模型时,运行以下命令:

alembic revision --autogenerate -m "Initial migration"

这将在 migrations/versions/ 目录中生成类似于:

migrations/versions/
│── 20240101123045_initial_migration.py

示例迁移文件 (20240101123045_initial_migration.py)

from alembic import op
import sqlalchemy as sa

# 迁移 ID(自动生成)
revision = '20240101123045'
down_revision = None  # 如果是第一个迁移,设置为 None
branch_labels = None
depends_on = None

def upgrade():
    """数据库升级操作"""
    op.create_table(
        'users',
        sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True),
        sa.Column('username', sa.String(length=50), nullable=False, unique=True),
        sa.Column('email', sa.String(length=100), nullable=False, unique=True),
        sa.Column('hashed_password', sa.String(length=200), nullable=False),
    )

def downgrade():
    """数据库降级操作(回滚)"""
    op.drop_table('users')

该文件:

​ • upgrade():定义数据库表的新增、修改操作。

​ • downgrade():定义数据库表的回滚操作。

5. 应用数据库迁移

创建迁移文件后,执行以下命令将其应用到数据库:

alembic upgrade head

​ • 成功后,数据库中会创建 users 表。

如果需要回滚数据库(撤销迁移),可以执行:

alembic downgrade -1

或者回滚到指定版本:

alembic downgrade <revision_id>

6. 如何在 Docker 里运行数据库迁移?

如果你使用 Docker 进行部署,在 docker-compose.yml 里,你可以使用 entrypoint.sh 自动执行数据库迁移:

#!/bin/sh

# 等待数据库启动
sleep 5

# 运行 Alembic 迁移
alembic upgrade head

# 启动 FastAPI 应用
exec uvicorn app.main:app --host 0.0.0.0 --port 8000

然后在 Dockerfile 里添加:

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

7. 总结

数据库迁移代码存放在 migrations/ 目录下

env.py 负责连接数据库和 ORM 绑定

使用 alembic revision --autogenerate 生成迁移文件

使用 alembic upgrade head 执行数据库迁移

可以在 Docker 部署时自动执行迁移

这样,你的 FastAPI 项目就可以安全地进行数据库结构变更,保持版本一致性!🚀

posted on 2025-02-02 17:50  朝朝暮Mu  阅读(719)  评论(0)    收藏  举报