[和訳] 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
複数データベースのための簡単な設定
-
pylons
Pylons
プロジェクト環境から読み込まれる設定
.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_encoding
Alembic が
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