Alembic 数据库迁移教程
1. 简介
Alembic 是一个数据库迁移工具,专门为 SQLAlchemy 设计。它可以帮助我们:
- 跟踪数据库模式的变化
- 管理数据库版本
- 在不同环境中同步数据库结构
- 回滚数据库变更
2. 安装
pip install alembic
3. 初始化
在项目根目录下执行:
alembic init alembic
这将创建以下文件和目录:
alembic/
├── env.py # 主要配置文件
├── README # 说明文件
├── script.py.mako # 迁移脚本模板
└── versions/ # 存放迁移脚本的目录
alembic.ini # Alembic 配置文件
4. 配置
4.1 修改 alembic.ini
找到 sqlalchemy.url
配置项,修改为你的数据库连接 URL:
sqlalchemy.url = mysql://username:password@localhost/dbname
4.2 修改 env.py
在 env.py
中导入你的模型:
from models.base import Base
target_metadata = Base.metadata
5. 常用命令
5.1 创建迁移脚本
# 自动检测模型变化并生成迁移脚本
alembic revision --autogenerate -m "描述信息"
# 创建空的迁移脚本
alembic revision -m "描述信息"
5.2 执行迁移
# 更新到最新版本
alembic upgrade head
# 更新到指定版本
alembic upgrade <revision>
# 回滚到上一个版本
alembic downgrade -1
# 回滚到指定版本
alembic downgrade <revision>
# 回滚到初始状态
alembic downgrade base
5.3 查看信息
# 查看当前版本
alembic current
# 查看历史版本
alembic history
# 查看历史版本(详细信息)
alembic history -v
6. 迁移脚本示例
"""add user table
Revision ID: 1234567890ab
Revises:
Create Date: 2024-01-23 10:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers
revision = '1234567890ab'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# 创建表
op.create_table(
'users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('username', sa.String(50), nullable=False),
sa.Column('email', sa.String(100), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('username'),
sa.UniqueConstraint('email')
)
# 添加列
op.add_column('users', sa.Column('created_at', sa.DateTime))
# 创建索引
op.create_index('idx_username', 'users', ['username'])
def downgrade():
# 删除索引
op.drop_index('idx_username', 'users')
# 删除列
op.drop_column('users', 'created_at')
# 删除表
op.drop_table('users')
7. 最佳实践
7.1 迁移前的准备
- 确保所有模型变更已完成
- 备份数据库
- 在测试环境中先测试迁移脚本
7.2 编写迁移脚本的建议
- 每个迁移脚本只做一件事
- 为迁移脚本写清晰的描述
- 确保
upgrade()
和downgrade()
函数是对应的 - 测试
downgrade()
函数
7.3 处理数据迁移
def upgrade():
# 1. 创建新表
op.create_table(...)
# 2. 获取连接
connection = op.get_bind()
# 3. 迁移数据
connection.execute(
"""
INSERT INTO new_table (id, name)
SELECT id, name FROM old_table
"""
)
# 4. 删除旧表
op.drop_table('old_table')
8. 常见问题
8.1 自动生成没有检测到变更
可能的原因:
- 模型没有正确导入
- 元数据对象配置错误
- 表已经存在于数据库中
解决方法:
- 检查
env.py
中的导入 - 确认
target_metadata
设置正确 - 手动编写迁移脚本
8.2 迁移执行失败
常见原因:
- 数据库连接问题
- SQL 语法错误
- 数据不兼容
处理方法:
- 检查错误信息
- 回滚到上一个版本
- 修复问题后重新迁移
9. 与 FastAPI 集成
在 FastAPI 项目中使用 Alembic:
# config.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "mysql://user:password@localhost/dbname"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(bind=engine)
# env.py
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
from config import SQLALCHEMY_DATABASE_URL
from models.base import Base
config = context.config
config.set_main_option("sqlalchemy.url", SQLALCHEMY_DATABASE_URL)
target_metadata = Base.metadata
def run_migrations_online():
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata
)
with context.begin_transaction():
context.run_migrations()