SQLAlchemy を用いた際に Alembic というマイグレーションツールを使ったので、導入方法を紹介します。
SQLAlchemyではコマンドでの操作やAlter Tableなどは行えませんが、
Alembicではコマンドを用いたスキーマの操作やAlter Tableを行うことができ、便利です。
Alembic とは
SQLAlchemy を使用した データベースmigrationライブラリです。
環境
pipenv, version 2021.5.29
python 3.9.5
手順
- 必要なライブラリをインストール
- alembic環境を作成
-
SQLAlchemyのモデルを作成
- migration スクリプトファイルをSQLAlchemyのモデルから自動で生成
1. 必要なライブラリをインストール
まずは下記ライブラリをインストールします。
1 |
pipenv install alembic sqlalchemy pymysql |
pymysqlはドライバーに設定するためにインストールします。
2. alembic環境を作成
下記コマンドを実行するとalembic.iniファイルとalembicディレクトリ配下にファイルとディレクトリが生成されます。
1 |
alembic init alembic_sample |
alembic.ini
alembic スクリプトが呼び出された時に検索されるファイルです。
env.py
このファイルは alembic スクリプトが呼び出された時に起動する Python スクリプトです。
migrationするためのSQLAlchemy のEngine などの設定が書かれています。
script.py.mako
versions配下に生成される migration のスクリプトの Mako テンプレートファイルです。
versions/
このディレクトリ配下にmigrationファイルが生成されます。
3. DBの設定を記述したsetting.pyを作成
ここにDBの設定を記述し、EngineとBaseを定義します。
setting.py
1 2 3 4 5 6 7 8 9 10 |
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base # Engine の作成 Engine = create_engine( "mysql+pymysql://root:@127.0.0.1:3306/alembic_sample", encoding="utf-8", echo=False ) Base = declarative_base() |
4. SQLAlchemyのモデルを作成
setting.pyで定義したBaseを使用し、UserModelを作成します。
user.py
1 2 3 4 5 6 7 8 9 |
from sqlalchemy import Column, String, Integer from setting import Base class UserModel(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(100), nullable=False) ) |
5. migration スクリプトファイルをSQLAlchemyのモデルから自動で生成
5-1. alembic.iniのDBのURLを変更
alembic.ini
1 |
sqlalchemy.url = driver://user:pass@localhost/dbname |
↓
1 |
sqlalchemy.url = mysql+pymysql://root:@localhost/alembic_sample |
※Databaseは各自の環境に合わせて設定してください。
5-2. env.pyにSQLAlchemyのモデルを読み込むように設定
env.pyにsetting.pyで定義したBaseをimportし、metadataに設定します。
UserModel は使いませんが 、import しないと Baseのmetadataにモデルが登録されず、
alembicでmigrationスクリプトファイルを作成した際に認識されないので注意が必要です。
env.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
from logging.config import fileConfig from sqlalchemy import engine_from_config from sqlalchemy import pool from alembic import context # 追加 from setting import Base # 追加 from user import UserModel # 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 = Base.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. """ 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() |
5-3. alembic revision ––autogenerateコマンドを実行
1 2 3 4 5 |
$ alembic revision --autogenerate -m "create tables" INFO [alembic.runtime.migration] Context impl MySQLImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.autogenerate.compare] Detected added table 'users' Generating /Users/tsujishougo/PycharmProjects/alembic_intro/alembic_sample/versions/3a2cc9966eae_create_tables.py ... done |
versions/配下に下記のmigrationスクリプトファイルができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
"""create tables Revision ID: 3a2cc9966eae Revises: Create Date: 2021-06-26 18:41:25.143285 """ from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = '3a2cc9966eae' 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(length=100), nullable=False), sa.PrimaryKeyConstraint('id') ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### op.drop_table('users') # ### end Alembic commands ### |
6. DBのテーブルにSQLAlchemyのモデルを反映
6-1. alembic upgrade headを実行
最後にalembic upgrade headを実行するとDBにSQLAlchemyのモデルがテーブルとして反映されます。
1 2 3 4 |
$ alembic upgrade head INFO [alembic.runtime.migration] Context impl MySQLImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.runtime.migration] Running upgrade -> 3a2cc9966eae, create tables |
終わりに
SQLAlchemyだけでは、Alter Tableなどできないので、Alembicを導入するとより効率的にスキーマの操作ができるようになりました。今後、ぜひSQLAlchemyを使う際は取り入れていきたいです。
参考
https://alembic.sqlalchemy.org/en/latest/index.html