­

Python 資料庫遷移工具 Alembic

  • 2019 年 11 月 10 日
  • 筆記

Alembic 是一款輕量型的資料庫遷移工具,它與 SQLAlchemy 一起共同為 Python 提供資料庫管理與遷移支援。

Alembic 的應用

Alembic 使用 SQLAlchemy 作為資料庫引擎,為關係型數據提供創建、管理、更改和調用的管理腳本,協助開發和運維人員在系統上線後對資料庫進行在線管理。

同任何 Python 擴展庫一樣,我們可以通過 pip 來快速的安裝最新的穩定版 Alembic 擴展庫 pip install alembic

創建 Alembic 遷移環境

在使用 Alembic 之前需要先建立一個 Alembic 腳本環境,通過在工程目錄下輸入 alembic init alembic 命令可以快速在應用程式中建立 Alembic 腳本環境,當在命令行看到以下輸出時,表示 alembic 腳本環境創建完成。

(.venv) ➜  server alembic init alembic    Creating directory /<path>/alembic ...  done    Creating directory /<path>/versions ...  done    Generating /<path>/alembic/script.py.mako ...  done    Generating /<path>/alembic/env.py ...  done    Generating /<path>/alembic/README ...  done    Generating /<path>/alembic.ini ...  done    Please edit configuration/connection/logging settings in '/Users/keinYe/Work/python/server/alembic.ini' before proceeding.

你可以通過 -t 選項來選擇一個初始化的模板,Alembic 目前支援三個初始化模板「通過 alembic list_templates 可以查看支援的模板類型」,默認情況下使用的是通用模板,在大多數情況下使用通用模板即可。

生成的遷移腳本目錄如下:

├── alembic  │   ├── README  │   ├── env.py  │   ├── script.py.mako  │   └── versions
  • alembic 目錄:遷移腳本的根目錄,放置在應用程式的根目錄下,可以設置為任意名稱。在多資料庫應用程式可以為每個資料庫單獨設置一個 Alembic 腳本目錄。
  • README 文件:說明文件,初始化完成時沒有什麼意義。
  • env.py 文件:一個 python 文件,在調用 Alembic 命令時該腳本文件運行。
  • script.py.mako 文件:是一個 mako 模板文件,用於生成新的遷移腳本文件。
  • versions 目錄:用於存放各個版本的遷移腳本。初始情況下為空目錄,通過 revision 命令可以生成新的遷移腳本。

alembic 會在你的應用程式的根目錄下生成一個 alembic.ini 的配置文件,在開始任何的操作之前需要先修改該文件中的 sqlalchemy.url 指向你自己的資料庫地址。

生成遷移腳本

當 Alembic 配置環境創建完成後,可以通過 Alembic 的子命令 revision 來生成新的遷移腳本。

(.venv) ➜  server alembic revision -m "first create script"    Generating /<path>/alembic/versions/4d1cbe22bfc6_first_    create_script.py ...  done

初始的遷移腳本中並沒有實際有效的內容,相當於一個空白的模板文件「增加了版本資訊」。如果對整改工程的數據表進行修改後,再次運行 revision 子命令可以看到新生成的腳本文件中的內容增加了我們對數據表的改變內容。

(.venv) ➜  server alembic revision -m "add create date in user table"    Generating /<path>/alembic/versions/fc540ca3e448_add_    create_date_in  _user_table.py ...  done

此時在 alembic 文件夾中可以看到以下文件:

alembic├── README  ├── env.py  ├── script.py.mako  └── versions      ├── 3602707b314b_first_create_script.py      └── eac6fb06ced5_add_create_date_in_user_table.py

可以看到在 versions 目錄中生成了兩個遷移腳本文件,但是此時的遷移腳本文件中沒有任何的有效程式碼,文件內容如下:

"""add create date in user table  Revision ID: eac6fb06ced5  Revises: 3602707b314b  Create Date: 2019-11-03 06:54:23.862575  """  from alembic  import opimport sqlalchemy as sa  # revision identifiers, used by Alembic.  revision = 'eac6fb06ced5'  down_revision = '3602707b314b'  branch_labels = None  depends_on = None  def upgrade():      pass  def downgrade():      pass

在該文件中制定了當前版本號 revision 和父版本號 down_revision ,以及相應的升級操作函數 upgrade 和降級操作函數 dwongrade。在 upgradedwongrade 函數中通過相應的 API 來操作 op 和 sa 對象來完成對資料庫的修改,以下程式碼完成了在資料庫中新增一個 account 數據表的功能。

def upgrade():      op.create_table(          'account',          sa.Column('id', sa.Integer, primary_key=True),          sa.Column('name', sa.String(50), nullable=False),          sa.Column('description', sa.Unicode(200)),      )  def downgrade():      op.drop_table('account')

每次編寫完程式碼還需要手動編寫遷移腳本這並不是程式設計師所需要的,幸運的是 Alembic 的開發者為程式設計師提供了更美好的操作「自動生成遷移腳本」。自動生成遷移腳本無需考慮資料庫相關操作,只需完成 ROM 中相關類的編寫即可,通過 Alembic 命令即可在資料庫中自動完成數據表的生成和更新。在 Alembic 中通過 revision 子命令的 –autogrenerate 選項參數來生成自動遷移腳本。

在使用自動生成命令之前,需要在 env.py 文件中修改 target_metadata 配置使其指嚮應用程式中的元數據對象。

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

在 user 數據表中新增 create_date 數據列,然後使用自動生成遷移腳本命令,查看我們的配置是否完成。運行命令後可以看到以下資訊:

(.venv) ➜  server alembic revision --autogenerate -m 'add create date in user table'  INFO  [alembic.runtime.migration] Context impl SQLiteImpl.  INFO  [alembic.runtime.migration] Will assume non-transactional DDL.  INFO  [alembic.autogenerate.compare] Detected added column 'user.create_date'    Generating /<path>/alembic/versions/476cb25a3138_add_create_date_in_user_table.py ...  done

生成的遷移腳本文件內容如下:

def upgrade():      # ### commands auto generated by Alembic - please adjust! ###      op.add_column('user', sa.Column('create_date', sa.DateTime(), nullable=True))      # ### end Alembic commands ###  def downgrade():      # ### commands auto generated by Alembic - please adjust! ###      op.drop_column('user', 'create_date')      # ### end Alembic commands ###

在自動生成遷移腳本的過程中你可能會遇到以下錯誤

(.venv) ➜  server alembic revision --autogenerate  INFO  [alembic.runtime.migration] Context impl SQLiteImpl.  INFO  [alembic.runtime.migration] Will assume non-transactional DDL.  ERROR [alembic.util.messaging] Target database is not up to date.    FAILED: Target database is not up to date.

出現該錯誤的原因是沒有使用 Alembic 更新資料庫,如果你沒有手動創建數據表可以使用 alembic upgrade head 命令消除該錯誤,如果你已經通過命令行或其他方式創建了數據表,可以使用 alembic stamp head 命令來設置 Alembic 的狀態。

變更資料庫

Alembic 最重要的功能是自動完成資料庫的遷移「變更」,所做的配置以及生成的腳本文件都是為數據的遷移做準備的,資料庫的遷移主要用到 upgradedowngrade 子命令。

資料庫的變更主要用到以下命令:

  • alembic upgrade head:將資料庫升級到最新版本。
  • alembic downgradebase:將資料庫降級到最初版本。
  • alembic upgrade<version>:將資料庫升級到指定版本。
  • alembic downgrade<version>:將資料庫降級到指定版本。
  • alembic upgrade+2:相對升級,將資料庫升級到當前版本後的兩個版本。
  • alembic downgrade+2:相對降級,將資料庫降級到當前版本前的兩個版本。

以上所有的升降級方式都是在線方式實時更新資料庫文件,實際環境中總會存在一些環境無法在線升級,Alembic 提供了生成 SQL 腳本的形式,已提供離線升降級的功能。

alembic upgrade <version> --sql > migration.sql

alembic downgrade <version> --sql > migration.sql

隨著項目的進展,項目下不可避免的會生成很多版本的遷移腳本,此時可以使用 current 來查看線上資料庫處於什麼版本,也可以通過 history 來查看項目目錄中的遷移腳本資訊。

(.venv) ➜  server alembic current  INFO  [alembic.runtime.migration] Context impl SQLiteImpl.  INFO  [alembic.runtime.migration] Will assume non-transactional DDL.  71fe19b20211  (.venv) ➜  server alembic history --verbose  Rev: 476cb25a3138 (head)  Parent: 71fe19b20211  Path: /<path>/alembic/versions/476cb25a3138_add_create_date_in_user_table.py        add create date in user table        Revision ID: 476cb25a3138      Revises: 71fe19b20211      Create Date: 2019-11-03 07:20:23.594777    Rev: 71fe19b20211  Parent: <base>  Path: /<path>/alembic/versions/71fe19b20211_crate_user_table.py      crate user table        Revision ID: 71fe19b20211      Revises:      Create Date: 2019-11-03 07:19:03.221662

在 Flask 中使用 Alembic

在 Flask 可以通過 Flask-Migrate 快速集成 Alembic 的支援。

Flask-Migrate 是使用 Alembic 處理 Flask 應用中資料庫「使用 SQLAlchemy ORM」遷移的擴展庫。其內置了 Click 命令行程式,在 Flask 上可直接使用命令行工具進行資料庫的遷移。關於 Click 的使用請參考 Python 命令行神器 Click

Flask-Migrate 支援使用 pip 進行安裝:

pip install flask-migrate

Flask-Migrate 安裝完成後,會在 Flask 應用程式的命令下自動生成一個 db 子命令,我們通過該命令即可完成數據的操作。

(.venv) ➜  server server --help  Usage: server [OPTIONS] COMMAND [ARGS]...      This is the commandline interface for server.  Options:    --config CONFIG  using a path like '/path/to/xxxx.cfg'    --help           Show this message and exit.    Commands:    db      Perform database migrations.    initdb  Create a database and add the first user    routes  Show the routes for the app.    run     Run a development server.    shell   Run a shell in the app context.  (.venv) ➜  server server db --help  Usage: server db [OPTIONS] COMMAND [ARGS]...      Perform database migrations.    Options:    --help  Show this message and exit.    Commands:    branches   Show current branch points    current    Display the current revision for each database.    downgrade  Revert to a previous version    edit       Edit a revision file    heads      Show current available heads in the script directory    history    List changeset scripts in chronological order.    init       Creates a new migration repository.    merge      Merge two revisions together, creating a new revision file    migrate    Autogenerate a new revision file (Alias for 'revision...    revision   Create a new revision file.    show       Show the revision denoted by the given symbol.    stamp      'stamp' the revision table with the given revision; don't run...    upgrade    Upgrade to a later version

Flask-Migrate 的用法和 Alembic 類似,只是將 alembic 換成了你的應用名稱「或 flask」+ db 的方式。