DBマイグレーションツールのAlembicの使い方
Djangoようなフルスタックフレームワークにあるようなマイグレーション機能だけを切り出したツールとして、PythonにはAlembic
というマイグレーションツールがあります。
ここではAlembic
の基本的な使い方について解説します。また、ORMのSQLAlchemy
との併用の仕方についても解説します。
ここではDBはsqlite
を使用します。
インストール
まずはPythonの仮想環境を作成します。ここではvenvを使用します。
$ python -m venv "python環境名"
# ここではvenvという名前で作成
$ python -m venv venv
$ source ./venv/bin/activate
alembic
をインストールする。
$ pip install alembic
環境のセットアップ
次にalembic
の環境を作成していきましょう。以下のinit
で、alembicのプロジェクトとが作成されます。
$ alembic init "マイグレーション環境名"
# migrationsという名前で作成
$ alembic init migrations
migrations
というディレクトリが作成され、中身を見ると以下のようなファイル類が作られているはずです。
$ tree
.
├── alembic.ini
└── migrations
├── README
├── env.py
├── script.py.mako
└── versions
alembic.ini
alembicのスクリプトが実行される時
に読まれる構成ファイル。実行時の設定を記述する。
- env.py の場所
- log の出力
- migration ファイルの命名規則
env.py
マイグレーションツールが実行された時に必ず実行されるPythonスクリプト。
SQLAlchemyのEngineを設定や生成を行って、migrationが実行できるようにカスタマイズする。
script.py.mako
新しいmigrationのスクリプトを生成するために使用される Mako テンプレートファイル。
ここにあるものは何でもversions内の新しいファイルを生成するために使用される。
versions/
migrationスクリプトが保存される
ディレクトリ
.ini ファイルを編集する
alembic.ini
が別のディレクトリにある場合は、--config
オプションで指定することで参照させることができる。
alembic.init
にある、sqlalchemy.url
にSQLAlchemy が接続するためのURLを、実行する環境に応じて修正する。
sqlalchemy.url = sqlite:///test.db
SQLAlchemyの設定を行う
データベースの設定を行うsettings.py
をトップディレクトリに作成します。
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
path = 'sqlite:///db.sqlite3'
# path = 'mysql+pymysql://root:@127.0.0.1:3306/alembic_sample'
# Engine の作成
Engine = create_engine(
path,
encoding="utf-8",
echo=False
)
Base = declarative_base()
作成すると以下のようなディレクトリ構成になります。
$ tree
.
├── alembic.ini
├── migrations
│ ├── README
│ ├── env.py
│ ├── script.py.mako
│ └── versions
└── settings.py
SQLAlchemyのモデルを作成
次にマイグレーションファイルで自動で生成してもらうための、モデルを作成していきます。
models.py
を作成して、以下の内容を記述します。
from sqlalchemy import Column, Integer, String
from settings import Base
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
fullname = Column(String)
nickname = Column(String)
def __repr__(self):
return "<User('name={}', fullname={}, nickname={})>".format(
self.name,
self.fullname,
self.nickname
)
ここではユーザー情報であるusers
というテーブルを作成するためのモデルを作成します。
alembicの環境設定ファイルであるenv.pyを修正する
env.py
のtarget_metadata
を修正します。
settings.py
のBaseをインポートして、Base.metada
を設定します。
from settings import Base
target_metadata = Base.metadata
もし複数のモデルクラスを参照する必要があれば、次のように設定します。
from models import User
from models import User2
target_metadata = [
User.metadata, User2.metadata
]
env.py
にあるrun_migrations_online
を以下のように修正します。追記したのは、url = config.get_main_option("sqlalchemy.url")
と、context.configure
にurlを追加します。
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
# 追記
url = config.get_main_option("sqlalchemy.url")
with connectable.connect() as connection:
context.configure(
# 追記
url=url,
connection=connection,
target_metadata=target_metadata
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
マイグレーションファイルを自動生成する
以下のコマンドでマイグレーションファイルを自動生成する。
--autogenerate
というオプションを付けることで、SQLAlchemyのモデルの内容を元にマイグレーションファイルを自動生成する。
$ alembic revision --autogenerate -m "create tables"
versions/
配下に下記のmigrationスクリプトファイルが出来上がる。
"""create tables
Revision ID: 11e6228374a5
Revises:
Create Date: 2022-03-18 10:37:43.272548
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '11e6228374a5'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=True),
sa.Column('fullname', sa.String(), nullable=True),
sa.Column('nickname', sa.String(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('users')
# ### end Alembic commands ###
DBにマイグレーションファイルの内容を反映する
alembic upgrade head
を実行することで、マイグレーションファイルの内容をデータベースに反映させることができる。
$ alembic upgrade head
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> 11e6228374a5, create tables
DBの中身を見てみると以下の2つのテーブルが作られているはずです。
- alembic_version
- users
alembic_version
の中身はマイグレーション情報が入っています。
head
は最新のversionまでmigrationを実行する。
1 つだけ version を上げたい場合は、headではなく+1を使用します。
version を下げたいときは downgrade
コマンドを使用します。
現在の状態を調べる
$ alembic current
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
11e6228374a5 (head)
履歴を見る(history)
$ alembic history
更新の巻き戻し(downgrade)
##
# None -> 8d4b3cb61f6 -> 3f1cb7f09b7a という順序で更新が行われている。
# 現在の状態は3f1cb7f09b7a
##
# 一つ前の状態へ戻す
$ alembic downgrade 8d4b3cb61f6
INFO [alembic.context] Context impl SQLiteImpl.
INFO [alembic.context] Will assume transactional DDL.
# 現在の状態。8d4b3cb61f6
$ alembic current
INFO [alembic.context] Context impl SQLiteImpl.
INFO [alembic.context] Will assume transactional DDL.
Current revision for sqlite:////home/podhmo/.virtualenvs/pyramid/alembic_sample/data.db: None -> 8d4b3cb61f6, create
# baseを指定すると最初に戻す
$ alembic downgrade base
INFO [alembic.context] Context impl SQLiteImpl.
INFO [alembic.context] Will assume transactional DDL.
INFO [alembic.context] Running downgrade 8d4b3cb61f6 -> None
# 現在の状態。None(最初)
$ alembic current
INFO [alembic.context] Context impl SQLiteImpl.
INFO [alembic.context] Will assume transactional DDL.
Current revision for sqlite:////home/podhmo/.virtualenvs/pyramid/alembic_sample/data.db: None
# 元の状態に戻す 3f1cb7f09b7a
$ alembic upgrade 3f1cb7f09b7a
INFO [alembic.context] Context impl SQLiteImpl.
INFO [alembic.context] Will assume transactional DDL.
INFO [alembic.context] Running upgrade None -> 8d4b3cb61f6
INFO [alembic.context] Running upgrade 8d4b3cb61f6 -> 3f1cb7f09b7a
Discussion
こちら参考にさせていただきました(ありがとうございます)
一点、詰まったところがあったので残しておきます。
env.pyの設定のところですが、