Alembic

起步

安裝

pip install alembic

# 初始化
alembic init alembic

配置

  • 修改配置alembic.ini
[alembic]
sqlalchemy.url = driver://user:pass@localhost/dbname
# 如下,修改為當(dāng)前使用的sql數(shù)據(jù)庫連接url
# sqlalchemy.url = postgresql://user:pass@localhost/dbname
  • 修改配置alembic/env.py
    • 搜索"target_metadata"找到代碼
    • 如下是使用SQLModel
# 導(dǎo)入SQLModel,及自定義模型
from sqlmodel import SQLModel
from model.user import User

# target_metadata = None
target_metadata = SQLModel.metadata

遷移

  • 創(chuàng)建遷移數(shù)據(jù)
    • 通過對(duì)比數(shù)據(jù)庫表信息與所有遷移文件,計(jì)算出差異字段后再生成新的遷移文件
  • 首次使用,如果數(shù)據(jù)庫中已經(jīng)有表,會(huì)影響首次遷移文件
    • 建議先連接空數(shù)據(jù)庫,生成遷移文件(記錄下alembic_version表的生成SQL,在數(shù)據(jù)庫中插入表)
    • 將版本號(hào)加到"alembic_version"表中,就不會(huì)運(yùn)行重復(fù)執(zhí)行了
# 創(chuàng)建新記錄,并添加表格遷移信息(--autogenerate)
alembic revision -m "add_xxx" --autogenerate
# 在alembic/versions目錄下生成一個(gè)遷移文件

# 升級(jí)
alembic upgrade head
# 創(chuàng)建almebic_version的表(如果不存在)
# 應(yīng)用遷移文件

# 僅打印sql文件,但暫不修改數(shù)據(jù)庫
alembic upgrade head --sql


# 降級(jí)
alembic downgrade <version>
# version在生成遷移文件里,找到down_revision字段
  • 手動(dòng)創(chuàng)建數(shù)據(jù)表如下
    • 最好是連接空數(shù)據(jù)庫,再記錄生成的SQL語句(需要引擎echo=True)
# 手動(dòng)插入alembic數(shù)據(jù)表(Postgresql)
CREATE TABLE alembic_version (
        version_num VARCHAR(32) NOT NULL,
        CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num)
);

# 插入版本號(hào)
INSERT INTO alembic_version (version_num) VALUES ('2fee77670444');

遷移

生成默認(rèn)值

  • 想在已有數(shù)據(jù)的表中增加列,新增的列因?yàn)樾阅軉栴}不為空,需要設(shè)置個(gè)默認(rèn)值
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"字段在代碼端生效,比如插入記錄時(shí),賦值默認(rèn)值
    * "server_default"是設(shè)置數(shù)據(jù)庫端的默認(rèn)值,生成"... DEFAULT '0' ..."的SQL語句
    * "server_default"只能是str/ClauseElement/TextClause(報(bào)錯(cuò)會(huì)打印),將字符串設(shè)置為SQL語句中單引號(hào)內(nèi)得值即
# ...略

def upgrade() -> None:
    op.add_column(
        "user",
        sa.Column(
            "age",
            sa.Integer(),
            nullable=False,
            comment="年齡",
            server_default="0",  # 手動(dòng)添加該默認(rèn)值字段(使用default無效,因?yàn)槭谴a端生效)
        ),
    )
    # 或者直接寫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]
# 文件命名格式,默認(rèn)版本號(hào)是隨機(jī)的不方便查看,建議如下設(shè)置,文件格式"20241206-init-c28e8488d2bc.py"
file_template = %%(year)d%%(month).2d%%(day).2d-%%(slug)s-%%(rev)s

# 數(shù)據(jù)庫連接字符串,項(xiàng)目中不建議使用,而是在"alembic/env.py"切換到統(tǒng)一環(huán)境變量管理
sqlalchemy.url = driver://user:pass@localhost/dbname

alembic/env.py

  • 設(shè)置元數(shù)據(jù)
# 初始值
# target_metadata = None


# 修改為
from sqlmodel import SQLModel   # 或者導(dǎo)入sqlalchemy對(duì)應(yīng)的metadata
# 導(dǎo)入其他表對(duì)象,如
# from models import User

target_metadata = SQLModel.metadata
  • 數(shù)據(jù)庫連接
# 該方法在"alembic upgrade head --sql"生效(有--sql參數(shù))
def run_migrations_offline() -> None:
    # url = config.get_main_option("sqlalchemy.url")
    # 數(shù)據(jù)庫連接從統(tǒng)一環(huán)境變量獲取
    from setting import get_setting
    url = get_setting().db_url

    # 僅修改url來源,其他代碼不變

# 該方法在"alembic upgrade head"生效(無--sql參數(shù))
def run_migrations_online() -> None:
    # connectable = engine_from_config(
    #     config.get_section(config.config_ini_section, {}),
    #     prefix="sqlalchemy.",
    #     poolclass=pool.NullPool,
    # )
    # 從自己項(xiàng)目導(dǎo)入數(shù)據(jù)庫引擎
    from database import engine
    connectable = engine

    # 僅修改engine(其實(shí)也是改url來源),其他代碼不變

參考文檔

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容