Alembic
大约 3 分钟
Alembic
- Alembic官方文档:https://alembic.sqlalchemy.org/en/latest/
- python数据库迁移
起步
安装
pip install alembic
# 初始化
alembic init alembic
配置
- 修改配置alembic.ini
[alembic]
sqlalchemy.url = driver://user:pass@localhost/dbname
# 如下,修改为当前使用的sql数据库连接url
# sqlalchemy.url = postgresql://user:pass@localhost/dbname
- 修改配置alembic/env.py
- 搜索"target_metadata"找到代码
- 如下是使用SQLModel
# 导入SQLModel,及自定义模型
from sqlmodel import SQLModel
from model.user import User
# target_metadata = None
target_metadata = SQLModel.metadata
迁移
- 创建迁移数据
- 通过对比数据库表信息与所有迁移文件,计算出差异字段后再生成新的迁移文件
- 首次使用,如果数据库中已经有表,会影响首次迁移文件
- 建议先连接空数据库,生成迁移文件(记录下alembic_version表的生成SQL,在数据库中插入表)
- 将版本号加到"alembic_version"表中,就不会运行重复执行了
# 创建新记录,并添加表格迁移信息(--autogenerate)
alembic revision -m "add_xxx" --autogenerate
# 在alembic/versions目录下生成一个迁移文件
# 升级
alembic upgrade head
# 创建almebic_version的表(如果不存在)
# 应用迁移文件
# 仅打印sql文件,但暂不修改数据库
alembic upgrade head --sql
# 降级
alembic downgrade <version>
# version在生成迁移文件里,找到down_revision字段
- 手动创建数据表如下
- 最好是连接空数据库,再记录生成的SQL语句(需要引擎echo=True)
# 手动插入alembic数据表(Postgresql)
CREATE TABLE alembic_version (
version_num VARCHAR(32) NOT NULL,
CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num)
);
# 插入版本号
INSERT INTO alembic_version (version_num) VALUES ('2fee77670444');
迁移
生成默认值
- 想在已有数据的表中增加列,新增的列因为性能问题不为空,需要设置个默认值
from sqlmodel import SQLModel, Field, BigInteger, Integer, String
class User(SQLModel, table=True):
__tablename__ = "user"
id: int | None = Field(sa_type=BigInteger, default=None, primary_key=True)
name: str = Field(sa_type=String(32), default="")
# 新增年龄这一列
age: int = Field(sa_type=Integer, default=0, sa_column_kwargs={"comment": "年龄"})
- 修改生成的代码,"age"字段中,增加"server_default" * "default"字段在代码端生效,比如插入记录时,赋值默认值 * "server_default"是设置数据库端的默认值,生成"... DEFAULT '0' ..."的SQL语句 * "server_default"只能是str/ClauseElement/TextClause(报错会打印),将字符串设置为SQL语句中单引号内得值即
# ...略
def upgrade() -> None:
op.add_column(
"user",
sa.Column(
"age",
sa.Integer(),
nullable=False,
comment="年龄",
server_default="0", # 手动添加该默认值字段(使用default无效,因为是代码端生效)
),
)
# 或者直接写SQL语句
op.execute("ALTER TABLE \"user\" ADD COLUMN age INTEGER DEFAULT '0' NOT NULL")
def downgrade() -> None:
op.drop_column("user", "age")
- 生成语句
ALTER TABLE "user" ADD COLUMN age INTEGER DEFAULT '0' NOT NULL
配置
alembic.ini
[alembic]
# 文件命名格式,默认版本号是随机的不方便查看,建议如下设置,文件格式"20241206-init-c28e8488d2bc.py"
file_template = %%(year)d%%(month).2d%%(day).2d-%%(slug)s-%%(rev)s
# 数据库连接字符串,项目中不建议使用,而是在"alembic/env.py"切换到统一环境变量管理
sqlalchemy.url = driver://user:pass@localhost/dbname
alembic/env.py
- 设置元数据
# 初始值
# target_metadata = None
# 修改为
from sqlmodel import SQLModel # 或者导入sqlalchemy对应的metadata
# 导入其他表对象,如
# from models import User
target_metadata = SQLModel.metadata
- 数据库连接
# 该方法在"alembic upgrade head --sql"生效(有--sql参数)
def run_migrations_offline() -> None:
# url = config.get_main_option("sqlalchemy.url")
# 数据库连接从统一环境变量获取
from setting import get_setting
url = get_setting().db_url
# 仅修改url来源,其他代码不变
# 该方法在"alembic upgrade head"生效(无--sql参数)
def run_migrations_online() -> None:
# connectable = engine_from_config(
# config.get_section(config.config_ini_section, {}),
# prefix="sqlalchemy.",
# poolclass=pool.NullPool,
# )
# 从自己项目导入数据库引擎
from database import engine
connectable = engine
# 仅修改engine(其实也是改url来源),其他代码不变