Skip to content

自動產生遷移

Alembic 可以查看資料庫狀態(透過 alembic.ini 檔案中的 sqlalchemy.url 指向,並使用當前 schema),並將其與應用程式中的 metadata(ORM 建議的 schema)進行比較,從而基於比較結果產生 "obvious" 的遷移。這是透過 alembic revision 命令的 --autogenerate 選項實現的,該選項會將所謂的 migration candidate 放入新的遷移檔案中。我們會根據需要手動審查和修改這些候選遷移,然後繼續執行後續步驟。

要使用自動產生(autogenerate)功能,我們首先需要修改 env.py 文件,使其能夠存取包含目標表的 metadata object。假設我們的應用程式在 myapp.mymodel 中有一個 declarative base。該 base 包含一個 MetaData 對象,其中包含定義資料庫的 Table 對象。我們需要確保在 env.py 中載入此對象,然後透過 target_metadata 參數將其傳遞給 EnvironmentContext.configure()

為了方便起見,通用範本中使用的 env.py 範例腳本頂部附近已經有一個變數聲明,我們將 None 替換為我們的 MetaData 物件。從以下程式碼開始:

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = None

我們改為:

from myapp.mymodel import Base

target_metadata = Base.metadata

Info

上述範例指的是通用的 alembic env.py 模板,例如呼叫 alembic init 時預設建立的模板,而不是像 multidb 這樣的特殊用途模板。有關 autogenerate metadata 的具體位置和方式,請直接查閱 env.py 腳本的原始碼和註解。

如果我們查看腳本後面的部分,在 run_migrations_online() 函數中,我們可以看到傳遞給 EnvironmentContext.configure() 的指令:

def run_migrations_online():
    engine = engine_from_config(
                config.get_section(config.config_ini_section), prefix='sqlalchemy.')

    with engine.connect() as connection:
        context.configure(
                    connection=connection,
                    target_metadata=target_metadata
                    )

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

然後我們可以結合使用 --autogenerate 選項來執行 alembic revision 指令。假設我們的元資料中包含了帳戶表的定義,而資料庫中沒有。我們會得到類似這樣的輸出:

$ alembic revision --autogenerate -m "Added account table"

INFO [alembic.context] Detected added table 'account'
Generating /path/to/foo/alembic/versions/27c6a30d7c24.py...done

然後我們可以查看檔案 27c6a30d7c24.py,發現其中已經存在一個基本的遷移腳本:

"""empty message

Revision ID: 27c6a30d7c24
Revises: None
Create Date: 2011-11-08 11:40:27.089406

"""

# revision identifiers, used by Alembic.
revision = '27c6a30d7c24'
down_revision = None

from alembic import op
import sqlalchemy as sa

def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.create_table(
        'account',
        sa.Column('id', sa.Integer()),
        sa.Column('name', sa.String(length=50), nullable=False),
        sa.Column('description', sa.VARCHAR(200)),
        sa.Column('last_transaction_date', sa.DateTime()),
        sa.PrimaryKeyConstraint('id')
    )
    ### end Alembic commands ###

def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_table("account")
    ### end Alembic commands ###

當然,遷移實際上還沒有執行過。我們透過常規的升級命令來完成遷移。我們還應該進入遷移文件,根據需要進行修改,包括調整指令以及添加其他可能依賴這些指令的指令 - 特別是創建/修改/刪除操作之間的資料變更。

What does Autogenerate Detect

絕大多數 Alembic 使用者問題都集中在自動產生功能能夠可靠地偵測到哪些類型的變更,以及它如何渲染偵測到的 Python 程式碼。

需要特別注意的是,自動生成功能並非完美無缺。用戶始終需要手動檢查並修正自動產生的候選遷移。隨著版本的不斷更新,該功能正變得越來越完善、越來越可靠,但使用者仍應注意其當前的限制。

自動生成功能將偵測到

  • Table additions, removals.
  • Column additions, removals.
  • Change of nullable status on columns.
  • Basic changes in indexes and explicitly-named unique constraints
  • Basic changes in foreign key constraints

自動生成功能可以選擇性地檢測

  • Change of column type: 除非將參數 EnvironmentContext.configure.compare_type 設為 False,否則預設會執行此操作。預設實作能夠可靠地偵測到主要變化,例如 NumericString 之間的變化,並且能夠相容於 SQLAlchemy 的「通用」類型(例如 Boolean)產生的類型。兩種類型共享的參數(例如長度和精度值)也會進行比較。如果元資料類型或資料庫類型具有另一種類型所不具備的額外參數,則不會進行比較。例如,如果一種數值類型具有「小數位數」而另一種類型沒有,則會被視為後端資料庫不支援該值,或報告元資料中未指定的預設值。

  • Change of server default: 如果您將 EnvironmentContext.configure.compare_server_default 參數設為 True 或自訂可呼叫函數,則會發生這種情況。此功能適用於簡單情況,但並非總是能產生準確的結果。 PostgreSQL 後端實際上會針對資料庫呼叫「detected」和「metadata」值來決定等效性。此功能預設為關閉,以便先在目標模式上進行測試。與類型比較類似,也可以透過傳遞可呼叫物件來自訂此功能;有關詳細信息,請參閱該函數的文檔。

自動生成功能無法偵測到

  • Changes of table name: 這些操作會表現為兩個不同表格的新增/刪除,應該手動編輯為名稱變更。
  • Changes of column name: 與表名變更類似,這些變更會被偵測為列的新增/刪除對,這與名稱變更完全不同。
  • Anonymously named constraints: 給你的約束條件取個名字,例如 UniqueConstraint('col1', 'col2', name="my_name")。
  • 當後端不支援 ENUM 類型時,產生的特殊 SQLAlchemy 類型(例如 Enum)可能會出現問題。這是因為在不支援 ENUM 的資料庫中,此類類型的表示形式(例如 CHAR+CHECK 限制)可能是任何類型的 CHAR+CHECK。 SQLAlchemy 只能透過猜測來判斷它是否真的是 ENUM,而這種猜測通常並不可取。為了實現自訂的「猜測」功能,可以使用 sqlalchemy.events.DDLEvents.column_reflect() 事件來偵測何時反射了 CHAR(或其他目標類型),並在確定其預期類型為 ENUM(或其他所需類型)時將其變更為 ENUM。在自動產生過程中,可以使用 sqlalchemy.events.DDLEvents.after_parent_attach() 事件來攔截並取消附加不需要的 CHECK 約束。

自動生成功能目前無法偵測到

  • 某些獨立約束的新增和刪除可能不受支持,包括 PRIMARY KEY、EXCLUDE 和 CHECK;這些約束未必已在自動產生檢測系統中實現,也可能不受支援的 SQLAlchemy 方言的支援。
  • 序列的新增和刪除——尚未實現。

值得注意的第三方函式庫,它們擴展了 Alembic 內建的自動生成功能:

  • alembic-utils 是一個為 PostgreSQL 函數、視圖、觸發器等新增自動產生支援的函式庫。
  • alembic-postgresql-enum 是一個為 PostgreSQL 枚舉的建立、修改和刪除新增自動產生支援的函式庫。