[和訳] Alembic チュートリアル
免責
非公式に和訳しました。必ずしも正しく和訳できているとは限りませんのでご容赦ください。参照したのは Alembic 1.5.5 のドキュメントのチュートリアルです。原文はこちらです。
チュートリアル
Alembicはリレーショナルデータベースのマネジメントスクリプトの生成、管理、実行を提供します。Alembic は SQLAlchemy を使用します。このチュートリアルではこのツールの使い方とその理論を説明します。
まず最初に Alembic は Installation で指定された方法でインストールがされているとします。 Installation で指定されたように、通常 Alembic は対象のプロジェクトと同じモジュール内、同じ Python パスにインストールされます。通常 Python の仮想環境を用い、 alembic の Python スクリプト env.py を実行する alembic コマンドがプロジェクトのモデルを読み込めるようにします。これは必ずしも全ての場合で必要とされるわけではありませんが、ほとんどの場合推奨されます。
このチュートリアルでは alembic コマンドラインユーティリティが実行されるときには、ローカルパスに存在し、プロジェクトの Python モジュールにアクセスできるものとします。
マイグレーション環境
マイグレーション環境のの作成から始目ましょう。これは、特定のアプリケーション固有のものです。このマイグレーション環境は一回だけ作成され、アプリケーションのソースコードと共に管理されます。この環境は Alembic の init コマンドによって作成され、アプリケーションの要件によってカスタマイズすることができます。
生成されたマイグレーション用のスクリプトを含むこの環境の構成は以下のようになります。
yourproject/
alembic/
env.py
README
script.py.mako
versoins/
3512b954651e_add_account.py
2b1ae634e5cd_add_order_id.py
3adcc9a56557_rename_username_field.py
-
yourprojectこれはアプリケーションソースコードのルートディレクトリかその中のディレクトリです。
-
alembicこのディレクトリはアプリケーションのソースコードの中に存在し、マイグレーション環境のホームディレクトリとなります。この命名は
alembicに限らず自由に行うことができ、プロジェクトが複数のデータベースを使用する場合には複数のディレクトリを扱うこともあります。 -
env.pyこのファイルは、Alembicのマイグレーションツールが実行された時に必ず実行されるPythonスクリプトになります。少なくとも、このファイルには SQLAlchemyエンジンを生成し設定する手続きが含まれ、SQLAlchemyにトランザクションへのコネクションを確立させ、そのコネクションを用いてマイグレーションエンジンが実行されます。
env.pyスクリプトはマイグレーション実行方法をカスタマイズ可能なものにするために生成される環境の一部です。コネクションの確立方法だけでなく、マイグレーション環境がどのように実行されるべきかを記述することができます。このスクリプトによって、複数のエンジンを実行するために編集することも、マイグレーション環境の実行に受け渡す引数を設定することも、アプリケーション固有のライブラリやモデルを読み込んで利用可能にすることもできます。Alembic は複数のユースケースに対応するために
env.pyの初期テンプレートを提供しています。 -
READMEテンプレートで作成される様々な環境が含まれます。READMEとしての情報を含んだドキュメントであるべきです。
-
script.py.makoこれは、マイグレーションスクリプトを自動生成する Mako のテンプレートファイルです。ここに記述されたものは
versions/内の新しいファイルの生成に用いられます。これは各マイグレーションファイルの構成をコントロール可能なものにするために編集することができます。その変更は、どのライブラリを標準でインポートするべきかを指定したり、upgrade()やdowngrade()関数の構成を変更したりすることができます。例えば、multidb環境では、upgrade_engine1()やupgrade_engine2()などのように異なる名前の複数の関数が生成されることを可能にします。 -
versions/このディレクトリは個々のバージョンのスクリプトを保持します。他のマイグレーションツールを使用しているユーザーはこのディレクトリ内のファイルが昇順の数値ではなく GUID を用いていることに気が付くでしょう。 Alembic では、バージョンに関するスクリプトを順序付けることは、スクリプト自体の中の表現に関係しており、理論的にはバージョンファイルを他のものとの間に挿入することが可能になっています。慎重な手作業によって行われるべきですが、異なるブランチからのマイグレーションをマージすることができます。
環境を作成する
この環境への基本的な理解ができたところで、 alembic init によって作成してみましょう。これは、一般的なテンプレートを用いた環境を構築します。
$ cd /path/to/yourproject
$ source /path/to/yourproject/.venv/bin/activate # ローカルの仮想環境を想定しています
$ alembic init alembic
Creating directory /path/to/yourproject/alembic...done
Creating directory /path/to/yourproject/alembic/versions...done
Generating /path/to/yourproject/alembic.ini...done
Generating /path/to/yourproject/alembic/env.py...done
Generating /path/to/yourproject/alembic/README...done
Generating /path/to/yourproject/alembic/script.py.mako...done
Please edit configuration/connection/logging settings in
'/path/to/yourproject/alembic.ini' before proceeding.
3つ目のコマンドは initコマンドが alembic という名前のマイグレーション環境のディレクトリを作成することを意味しています。
Alembic は他の環境のテンプレートも含んでいます。これらは list_templates コマンドによって確認できます。
$ alembic list_templates
Available templates:
generic - Generic single-database configuration.
multidb - Rudimentary multi-database configuration.
pylons - Configuration that reads from a Pylons project environment.
Templates are used via the 'init' command, e.g.:
alembic init --template pylons ./scripts
-
generic単一データベース用の一般的な設定
-
multidb複数データベースのための簡単な設定
-
pylonsPylonsプロジェクト環境から読み込まれる設定
.ini ファイルの編集
Alembicは init コマンドによって、カレントディレクトリに alembic.ini を生成します。このファイルは alembic スクリプトが実行される時に参照するファイルです。これは alembic コマンドが実行されるときのカレントディレクトリにある必要があります。別のディレクトリにある場合は --config オプションで指定することで参照させることができます。
generic 設定で生成される alembic.ini ファイルは以下のようになります。
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = alembic
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# sys.path path, will be prepended to sys.path if present.
# defaults to the current working directory.
# (new in 1.5.5)
prepend_sys_path = .
# timezone to use when rendering the date
# within the migration file as well as the filename.
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =
# max length of characters to apply to the
# "slug" field
# truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; this defaults
# to alembic/versions. When using multiple version
# directories, initial revisions must be specified with --version-path
# version_locations = %(here)s/bar %(here)s/bat alembic/versions
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
sqlalchemy.url = driver://user:pass@localhost/dbname
# post_write_hooks defines scripts or Python functions that are run
# on newly generated revision scripts. See the documentation for further
# detail and examples
# format using "black" - use the console_scripts runner,
# against the "black" entrypoint
# hooks=black
# black.type=console_scripts
# black.entrypoint=black
# black.options=-l 79
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
このファイルは Python の ConfigParser.SafeConfigParser オブジェクトを用いることで読み込むことが可能です。 %(here)s は置換可能な変数で、上で行った Alembic スクリプトへのパスのように、ディレクトリやファイルへの絶対パスを生成するために使用することができます。
このファイルは以下のような特性を持っています。
-
[alembic]これは Alembic が設定のために読み込むセクションです。 Alembic 自身は他の箇所を直接読み込むことはありません。この
alembicという名前は--nameフラグによってカスタマイズすることが可能です(詳しくは複数のAlembic環境を一つの .ini ファイルで実行する方法をご覧ください)。 -
script_locationこれは、 Alembic 環境の場所を示しています。これは通常ファイルシステムの場所を示し、相対パスか絶対パスのどちらかで指定されます。もしこの場所が相対パスであった場合は、これはカレントディレクトリからの相対パスとして読み込まれます。
これはいかなる場合にも Alembic が必要とする唯一のキーです。
alembic init alembicコマンドによる .ini ファイルの生成はディレクトリ名を自動でalembicと決定します。特殊変数%(here)sも%(here)s/alembicとして使用されます。自分自身を .egg ファイルへパッケージ化するアプリケーションをサポートするために、値をパッケージリソースとして指定することもでき、その場合は
resource_filename()がそのファイルを探すために用いられる。ここではコロンを含む絶対パスではないURIは、ファイル名ではなくリソース名として解釈される。 -
file_templateこれは新しいマイグレーションファイルの生成時に用いられる命名規則である。デフォルトの値が設定されているため、ここではコメントアウトされています。利用可能なトークンは、
-
%%(rev)s修正の識別子
-
%%(slug)s修正のメッセージの先頭の文字列
-
%%(year)d,%%(month).2d,%%(day).2d,%%(minute).2d,%%(second).2d作成された日付。
timezone設定がされていない限り、デフォルトではdatetime.datetime.now()から取得されます。
-
-
timezone任意で設定できるタイムゾーンの名前です。
UTCやEST5EDTなどを設定できます。これはマイグレーションファイルのコメントやファイル名のタイムスタンプに反映されます。もしtimezoneを設定した場合、作成される date オブジェクトはdatetime.datetime.now()ではなく、datetime.datetime.utcnow().replace( tzinfo=dateutil.tz.tzutc() ).astimezone( dateutil.tz.gettz(<timezone>) )で作成されます。
-
truncate_slug_lengthデフォルトは 40 文字で設定されています。これは "slug" フィールドに含まれる文字数の最大値になります。
-
sqlalchemy.urlこれは、SQLAlchemyを通して接続するデータベースのURLです。ここで設定される値は、
env.pyファイルが呼ばれた時のみに使用されます。 例えば "generic" のテンプレートでは、run_migrations_offline()関数の中で呼ばれるconfig.get_main_option("sqlalchemy.url")と、run_migrations_online()関数の中で呼ばれるengine_from_config(prefix="sqlalchemy.")はこのキーが参照される場所です。もし SQLAlchemy URL が他のソース(例えば環境変数や公開されているレジストリ)から参照されるべき場合は、データベースのURLを獲得するためにどんなメソッドを用いるべきかをenv.pyファイルに記述する必要があります。 -
revision_environmentこのフラグが
trueであれば、新しい修正のファイルを生成するときやalembic historyコマンドを走らせるときに、マイグレーション環境のスクリプトであるenv.pyが無条件で実行されることを示します。 -
soucelessこのフラグが
trueであれば、リビジョンファイルは各バージョンのディレクトリ内の.pycや.pyoファイルとして存在します。これらのリビジョンファイルはそれぞれがバージョンとして使用され、ソースレスのバージョン管理が可能になります。デフォルトのfalseのままにしておくと、.pyファイルのみがバージョンファイルとして使用されます。 -
version_locationsリビジョンファイルの場所を示すオプショナルなリストです。この設定によってリビジョンファイルが複数のディレクトリに同時に存在させることが可能になります。これを用いた例についての詳細は、 Working with Multiple Base を参照してください。
-
output_encodingAlembic が
script.py.makoファイルを用いて新しいマイグレーションファイルを作成する時に使用するエンコーディングを示します。デフォルトではutf-8が設定されています。 -
[loggers],[handlers],[formatters],[logger_*],[handler_*],[formatter_*]これらのセクションは全て Python の標準的なログギング設定の一部です。その仕組みは Configuration File Format のドキュメントに示されています。データベース接続の場合と同様に、ここでの設定は
env.py(自由に編集が可能です)にあるlogging.config.fileConfig()の結果として直接使用されます。
SQLAlchemy の URL を設定して、データベースは一つで、 generic の設定で開始する場合にはただ一つ必要な設定は
sqlalchemy.url = postgresql://scott:tiger@localhost/test
です。
マイグレーションスクリプトの作成
環境が整ったら alembic revision を用いて新しいリビジョンファイルを生成できます。
$ alembic revision -m "create account table"
Generating /path/to/yourproject/alembic/versions/1975ea83b712_create_accoun
t_table.py...done
1975ea83b712_create_account_table.py が生成されます。ファイルの中身は
"""create account table
Revision ID: 1975ea83b712
Revises:
Create Date: 2011-11-08 11:40:27.089406
"""
# revision identifiers, used by Alembic.
revision = '1975ea83b712'
down_revision = None
branch_labels = None
from alembic import op
import sqlalchemy as sa
def upgrade():
pass
def downgrade():
pass
となります。
このファイルはヘッダー情報、現在の変更とダウングレードした変更の識別子、Alembicのインポートと空の empty() と downgrade() 関数を含みます。この upgrade() 関数と downgrade() 関数を編集し、データベースの変更についてを記述する必要があります。通常 upgratde() 関数は必須で、 downgrade() 関数はダウングレード機能が必要な場合にのみ必要とされます。
down_revision の変数によって Alembic がマイグレーションがどのような順になっているかを知ることができます。次の変更を生成したときに、 down_revision 識別子は
# revision identifiers, used by Alembic.
revision = 'ae1027a6acf'
down_revision = '1975ea83b712'
のようになります。
Alembic が /versions ディレクトリに対して実行される度に、ディレクトリ内の全てのファイルを読み込みます。そして down_revision 識別子の情報に基づいてリストを構成します。 down_revision が None であるファイルが最初のファイルであると認識されます。理論上、もしマイグレーション環境が数千のマイグレーションを含む場合、この処理は立ち上げるのに時間を要する可能性があります。しかし実用上、古いマイグレーションファイルは削除されるべきです。どのようにこの処理を行うべきかについては、 Building an Up to Date Database from Scratch を参照してください。
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')
create_table() と drop_table() は Alembic の関数です。 Alembic は基本的なデータベースのマイグレーションの手続きに関する関数を提供しています。それらの関数のほとんどは現在のテーブルのメタデータに依存せず、データベースに接続する方法を示す大域的なコンテキストを使用しています。マイグレーションを SQL/DDL の操作をファイルに出力することもできます。
Alembic のマイグレーションの関数に関しては、 Operation Reference を参照してください。
最初のマイグレーション実行
それでは最初のマイグレーションを実行します。データベースにはなんの情報もなく、バージョン管理もされていないと仮定します。 alembic upgrade コマンドがアップグレードの手続きを行い、現在のデータベースの状況(この例では None)から与えられた変更を実行します。マイグレーションの識別子 1975ea83b712 を指定することで特定の変更を反映させることもできますが、ほとんどの場合もっとも最新の情報を指定することが簡単です。この場合は head で最新であることを指定することができます。
$ alembic upgrade head
INFO [alembic.context] Context class PostgresqlContext.
INFO [alembic.context] Will assume transactional DDL.
INFO [alembic.context] Running upgrade None -> 1975ea83b712
ここで出力される情報は alembic.ini のログの設定を用いています。標準エラーを含めて alembic のストリームをコンソールにロギングします。
この処理において Alembic は最初に alembic_version テーブルが存在するかどうかを確認し、存在しない場合は作成します。最初に現在のバージョンを確認し、もしあればそのバージョンからリクエストされたマイグレーションまでのバージョンを計算します。この場合は head が指定されており、 1975ea83b712 のバージョンとして解釈されます。次にそれぞれのファイルの upgrade() メソッドが実行されます。
二回目のマイグレーション実行
それではもう一つのマイグレーションを実行してみましょう。再びマイグレーションファイルを作成します。
$ alembic revision -m "Add a column"
Generating /path/to/yourapp/alembic/versions/ae1027a6acf_add_a_column.py...
done
このファイルを編集し、 account テーブルにカラムを追加してみます。
"""Add a column
Revision ID: ae1027a6acf
Revises: 1975ea83b712
Create Date: 2011-11-08 12:37:36.714947
"""
# revision identifiers, used by Alembic.
revision = 'ae1027a6acf'
down_revision = '1975ea83b712'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('account', sa.Column('last_transaction_date', sa.DateTime))
def downgrade():
op.drop_column('account', 'last_transaction_date')
再び head に対して実行します。
$ alembic upgrade head
INFO [alembic.context] Context class PostgresqlContext.
INFO [alembic.context] Will assume transactional DDL.
INFO [alembic.context] Running upgrade 1975ea83b712 -> ae1027a6acf
これで last_transaction_date カラムが追加されました。
マイグレーション識別子を省略して参照させる
マイグレーション識別子はその一部を用いることでも指定できます。ただしこの文字列は一意にバージョンを指定できる長さである必要があります。バージョン番号を指定する箇所ではどこでも省略した識別子を指定することができます。
$ alembic upgrade ae1
ここで使った ae1 は ae1027a6acf を参照しています。複数のバージョンが参照されている場合には Alembic は処理を中断し、それを出力します。
マイグレーションの相対参照
アップグレードやダウングレードを実行する時に相対的な指定もすることもできます。例えば二つの先のバージョンを指定する場合は数値で +N と指定することでできます。
$ alembic upgrade +2
負の値を指定すればダウングレードに対しても指定できます。
$ alembic downgrade -1
相対的な識別子は特定のバージョンを起点にすることもできます。例えば ae1027a6acf から二つ先のバージョンを指定する場合は
$ alembic upgrade ae10+2
とすることができます。
情報の取得
二つのバージョンを作成しましたが、現在の情報を取得することもできます。
まず現在のマイグレーション情報をみてみます。
$ alembic current
INFO [alembic.context] Context class PostgresqlContext.
INFO [alembic.context] Will assume transactional DDL.
Current revision for postgresql://scott:XXXXX@localhost/test: 1975ea83b712 -> ae1027a6acf (head), Add a column
head は現在のデータベースと一致していることを示しています。
alembic history によって履歴を確認することもできます。 --verbose オプションを指定すれば各マイグレーションの全情報を取得できます。このオプションは history だけでなく、 current 、 heads 、 branches コマンドでも使えます。
$ alembic history --verbose
Rev: ae1027a6acf (head)
Parent: 1975ea83b712
Path: /path/to/yourproject/alembic/versions/ae1027a6acf_add_a_column.py
add a column
Revision ID: ae1027a6acf
Revises: 1975ea83b712
Create Date: 2014-11-20 13:02:54.849677
Rev: 1975ea83b712
Parent: <base>
Path: /path/to/yourproject/alembic/versions/1975ea83b712_add_account_table.py
create account table
Revision ID: 1975ea83b712
Revises:
Create Date: 2014-11-20 13:02:46.257104
指定の履歴を閲覧する
alembic history で -r オプションを使えばマイグレーションのバージョンのスライスを指定できます。 -r の引数は [start]:[end] で指定します。 start や end はバージョンの識別子や head, heads, base, current によって現在のバージョンを指定することができます。また、 start は負の数値で end に対する相対参照をすることができ、 end は正の数値で start に対する相対参照をすることができます。
$ alembic history -r1975ea:ae1027
相対的な範囲の指定は
$ alembic history -r-3:current
のように指定します。 head を省略することで 1975 から現在のバージョンまでを取得することができます。
$ alembic history -r1975ea:
ダウングレードを行う
ダウングレードをしたい場合は、 alembic downgrade コマンドを使用します。全てのマイグレーションをダウングレードする場合には base を指定します。
$ alembic downgrade base
INFO [alembic.context] Context class PostgresqlContext.
INFO [alembic.context] Will assume transactional DDL.
INFO [alembic.context] Running downgrade ae1027a6acf -> 1975ea83b712
INFO [alembic.context] Running downgrade 1975ea83b712 -> None
また最新までアップグレードするには
$ alembic upgrade head
INFO [alembic.context] Context class PostgresqlContext.
INFO [alembic.context] Will assume transactional DDL.
INFO [alembic.context] Running upgrade None -> 1975ea83b712
INFO [alembic.context] Running upgrade 1975ea83b712 -> ae1027a6acf
とします。
Discussion