目標
python の migration パッケージalembic · PyPIを動かしてみる
alembic とは??
Python で SQLAlchemy を使用しているときに DB の管理をしてくれる migration ツール
環境準備
Docker で環境を構築
手軽に構築したいので Docker で行う
.
├── README.md
├── docker-compose.yml
└── src
└── model.py
version: "3"
services:
db:
image: postgres:11.7
container_name: alembic-db
ports:
- 5432:5432
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=almebic
app:
image: python:3.8.2-slim
container_name: alembic-app
volumes:
- ./src:/usr/src
environment:
- PYTHONPATH=${PYTHONPATH}:/usr/src
tty: true
alembic のインストール
pip で必要なパッケージをインストールする
pip install alembic psycopg2-binary
alembic をインストールすれば SQLAlchemy も同時にインストールされる
psycopg2-binary · PyPI は postgres に接続するために使用する
root@9a7582105665:/usr/src# pip install alembic psycopg2-binary
Collecting alembic
Downloading alembic-1.4.2.tar.gz (1.1 MB)
|████████████████████████████████| 1.1 MB 7.8 MB/s
Installing build dependencies ... done
Getting requirements to build wheel ... done
Preparing wheel metadata ... done
Collecting psycopg2-binary
Downloading psycopg2_binary-2.8.5-cp38-cp38-manylinux1_x86_64.whl (3.0 MB)
|████████████████████████████████| 3.0 MB 32.3 MB/s
Collecting python-dateutil
Downloading python_dateutil-2.8.1-py2.py3-none-any.whl (227 kB)
|████████████████████████████████| 227 kB 23.8 MB/s
Collecting Mako
Downloading Mako-1.1.2-py2.py3-none-any.whl (75 kB)
|████████████████████████████████| 75 kB 11.2 MB/s
Collecting SQLAlchemy>=1.1.0
Downloading SQLAlchemy-1.3.16-cp38-cp38-manylinux2010_x86_64.whl (1.2 MB)
|████████████████████████████████| 1.2 MB 54.3 MB/s
Collecting python-editor>=0.3
Downloading python_editor-1.0.4-py3-none-any.whl (4.9 kB)
Collecting six>=1.5
Downloading six-1.14.0-py2.py3-none-any.whl (10 kB)
Collecting MarkupSafe>=0.9.2
Downloading MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl (32 kB)
Building wheels for collected packages: alembic
Building wheel for alembic (PEP 517) ... done
Created wheel for alembic: filename=alembic-1.4.2-py2.py3-none-any.whl size=159543 sha256=dc29f47f6c24908d9413da7e3c969c64c252d0cbf9f90fca7cfbb5782b2452d0
Stored in directory: /root/.cache/pip/wheels/70/08/70/cea787a7e95817b831469fa42af076046e55a05f7c94657463
Successfully built alembic
Installing collected packages: six, python-dateutil, MarkupSafe, Mako, SQLAlchemy, python-editor, alembic, psycopg2-binary
Successfully installed Mako-1.1.2 MarkupSafe-1.1.1 SQLAlchemy-1.3.16 alembic-1.4.2 psycopg2-binary-2.8.5 python-dateutil-2.8.1 python-editor-1.0.4 six-1.14.0
root@ecce2b20848e:/usr/src# pip list
Package Version
--------------- -------
alembic 1.4.2
Mako 1.1.2
MarkupSafe 1.1.1
pip 20.1
psycopg2-binary 2.8.5
python-dateutil 2.8.1
python-editor 1.0.4
setuptools 46.1.3
six 1.14.0
SQLAlchemy 1.3.16
wheel 0.34.2
alembic の環境を作成
alembic init で migration の環境の作成
alembic init {migrationの環境名}
で migration の環境を作成する
root@ecce2b20848e:/usr/src# alembic init migration
Creating directory /usr/src/migration ... done
Creating directory /usr/src/migration/versions ... done
Generating /usr/src/migration/README ... done
Generating /usr/src/alembic.ini ... done
Generating /usr/src/migration/env.py ... done
Generating /usr/src/migration/script.py.mako ... done
Please edit configuration/connection/logging settings in '/usr/src/alembic.ini' before proceeding.
作成が完了すると以下のような構造になる
migration ディレクトリ と alembic.ini ファイル ができたことが確認できる
.
├── README.md
├── docker-compose.yml
└── src
├── alembic.ini
├── migration
│ ├── README
│ ├── env.py
│ ├── script.py.mako
│ └── versions
└── model.py
alembic init で生成されるファイルについて
-
env.py
alembic のツールが起動する度に実行される Python のスクリプトが書かれている
SQLAlchemy の Engine を設定・生成し migration が実行できるように書かれている
カスタマイズ可能なスクリプト -
README.md
どのような環境で migration 環境を作成したか記述してある -
script.py.mako
新しい migration のスクリプトを生成するために使用される Mako テンプレートファイル
ここにあるものは何でも version/内の新しいファイルを生成するために使用される -
versions ディレクトリ
migration スクリプトが保存されるディレクトリ -
alembic.ini
alembic のスクリプトが実行された時に探すファイル
実行時の設定を記述する
ex. env.py の場所・log の出力・migration ファイルの命名規則など
migration の実行
alembic.ini の編集
DB に接続できるように alembic.ini ファイルを編集する
ini ファイルの以下の箇所を DB の接続情報に書き換える
sqlalchemy.url = driver://user:pass@localhost/dbname
sqlalchemy.url = postgresql://postgres:postgres@alembic-db:5432/almebic
接続情報は docker-compose.yml に記述してあるものを使用する
migration ファイルの作成
revision コマンドで migration ファイルを作成する
alembic revision -m {ファイル名}
root@ecce2b20848e:/usr/src# alembic revision -m "create account table"
Generating /usr/src/migration/versions/b5f586d58141_create_account_table.py ... done
実行後に versions ディレクトリ配下に migration ファイルが作成される
"""create account table
Revision ID: b5f586d58141
Revises:
Create Date: 2020-05-02 17:49:20.493493
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'b5f586d58141'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
pass
def downgrade():
pass
migration ファイルの編集
生成された migration ファイルを編集する
ここでは公式のをコピーして account テーブルを作成する
"""create account table
Revision ID: b5f586d58141
Revises:
Create Date: 2020-05-02 17:49:20.493493
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'b5f586d58141'
down_revision = None
branch_labels = None
depends_on = None
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')
migration の実行
upgrade コマンドで migration を実行する
alembic upgrade head
head は最新の version まで migration を実行する
1 つだけ version を上げたい場合は、head ではなく +1 を使用する
version を下げたいときは downgrade コマンドを使用する
初期状態まで戻したい場合は
alembic downgrade base
を実行する
一つ前の version に戻すときは、base ではなく -1 を使用する
root@ecce2b20848e:/usr/src# alembic upgrade head
INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO [alembic.runtime.migration] Will assume transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> b5f586d58141, create account table
migration のファイルを自動で生成させる
Auto Generating Migrations — Alembic 1.4.2 documentationにあるように env.py を編集することで
Python で定義している SQLAlchemy のモデルの情報から自動的に migration ファイルを作成できるようになる
まずは SQLAlchemy でモデルの定義を行う
SQLAlchemy のモデルの定義
モデルの定義では先ほど追加した account テーブルを SQLAlchemy のモデルで定義して
created_at と updated_at の列を追加する
env.py に SQLAlchemy の Engine を渡したいので定義する
from datetime import datetime
from sqlalchemy import create_engine, Column, String, Integer, Unicode, DateTime
from sqlalchemy.ext.declarative import declarative_base
# Engine の作成
Engine = create_engine(
"postgresql://postgres:postgres@alembic-db:5432/almebic",
encoding="utf-8",
echo=False
)
'''
モデルの Base を作成
この Base を基にモデルを定義するとmetadataにモデルの情報が格納されていく
'''
ModelBase = declarative_base()
class AcountModel(ModelBase):
"""
AcountModel
"""
__tablename__ = 'account'
id = Column(Integer, primary_key=True)
name = Column(String(50), nullable=False)
description = Column(Unicode(200))
created_at = Column(DateTime, default=datetime.now, nullable=False)
updated_at = Column(DateTime, default=datetime.now, nullable=False)
env.py の編集
env.py を編集して model.py で定義したモデルの情報を取得できるようにする
先頭で先ほど定義した ModelBase と Engine を import する
target_metadata に ModelBase.metadata を代入する
また、migration の実行時には run_migrations_online() を編集して migration が実行されるようにする
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
from model import ModelBase, Engine
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = ModelBase.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
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.
"""
url = config.get_main_option("sqlalchemy.url")
connectable = Engine
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 を使用した migration の実行
revision コマンドに --autogenerate というオプションを付けると SQLAlchemy で定義したモデルの情報から migration ファイルを作成する
root@9a7582105665:/usr/src# alembic revision --autogenerate -m "Added columns."
INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO [alembic.runtime.migration] Will assume transactional DDL.
INFO [alembic.ddl.postgresql] Detected sequence named 'account_id_seq' as owned by integer column 'account(id)', assuming SERIAL and omitting
INFO [alembic.autogenerate.compare] Detected added column 'account.created_at'
INFO [alembic.autogenerate.compare] Detected added column 'account.updated_at'
Generating /usr/src/migration/versions/dcd0d354f648_added_columns.py ... done
実行後に以下のようなファイルが作成される
"""Added columns.
Revision ID: dcd0d354f648
Revises: b5f586d58141
Create Date: 2020-05-02 18:58:03.864154
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'dcd0d354f648'
down_revision = 'b5f586d58141'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('account', sa.Column('created_at', sa.DateTime(), nullable=False))
op.add_column('account', sa.Column('updated_at', sa.DateTime(), nullable=False))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('account', 'updated_at')
op.drop_column('account', 'created_at')
# ### end Alembic commands ###
あとは upgrade コマンドで migration が完了する
おまけ
migration で生成されるファイルの名前を変えたい時は?
dcd0d354f648_added_columns.py
ではなく命名規則を変更したい時(日付の情報を含ませたいなど)は alembic.ini を編集する
alembic.ini の file_template を編集する
file_template = %%(year)d%%(month).2d%%(day).2d-%%(hour).2d%%(minute).2d_%%(slug)s
autogenerate はどんなな変更でも検知してくれるの?
一部検知してくれない変更もあるので注意が必要
詳しくは下記を参照
Auto Generating Migrations — Alembic 1.4.2 documentation