跳至主要內容

Alembic

言午日尧耳总大约 3 分钟PythonAlembic

Alembic

起步

安装

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来源),其他代码不变

参考文档

上次编辑于:
贡献者: xxc