SQLAlchemy+Alembic:モデルからマイグレーションファイルを自動生成
目次
- この記事について
- SQLAlchemyとAlembic
- 環境構築 〜インストールと設定〜
- 環境構築 〜SQLAlchemyを使ったモデルの作成〜
- 環境構築 〜Alembicでの複数モデルからのマイグレーション自動生成〜
- おまけ 〜ERAlchemyを使ったER図の自動生成〜
この記事について
この記事を読んで分かること
- SQLAlchemyとAlembicを用いてモデルからマイグレーションファイルを自動生成する方法
- 複数のモデルファイルを読み込んでマイグレーションを作成する方法
- SLQAlchemyを使ったモデルの書き方
この記事では詳しく書いていないこと
- SQLAlchemyとAlembicの概要
- SQLAlchemyのORMの使い方
ディレクトリ構造
最終的には実際のバックエンド側のAPIサーバーで使うことを想定して
このようなディレクトリ構造でマイグレーション環境を作ることを目標とします。
├── app │ ├── __init__.py │ └── models │ ├── team.py │ └── user.py ├── database │ └── migrations │ ├── __init__.py │ ├── env.py │ ├── script.py.mako │ └── versions │ └── b72a859457c7_create_tables.py └── alembic.ini
- モデルのファイルはapp/models/***.py
- マイグレーションファイルはdatabase/migrations/~
- alembic の設定ファイルはプロジェクト直下
SQLAlchemyとAlembic
SQLAlchemyの特徴
要は直接SQL文を書くことなくSQLite、MySQL 、PostgreSQL 等のデータベースを操作することができます。
Alembicの特徴
ちょっと前まではsqlalchemy-migrationというライブラリだったようです。
ただすでに開発はストップしており、後継者としてAlembicが開発されているようです。
環境構築〜インストールと設定〜
さぁ、それでは頑張って環境構築していきましょう!!
まずはライブラリのインストールと設定をしていきます。
SQLAlchemyインストール
- Python対応バージョン 2.7または3.4以上
$ pip install SQLAlchemy
Alembicインストール
$ pip install alembic
PyMySQLインストール
今回はデータベースクライアントとしてPyMySQLを使います。
$ pip install PyMySQL
これで今回のインストール完了です!
Alembic環境の初期化
以下のコマンドを打つとAlembic環境が構築されます。
(が、まだコマンドを打たずに読み進めて下さい)
$ alembic init migrations
├── alembic.ini ├── migrations ├── README ├── env.py ├── script.py.mako └── versions
ただ、今回はdatabase/migrationsというディレクトリ構造にしたいので下記のようにします
$ alembic init database/migrations
すると最初に説明したようなディレクトリ構造になったかと思います。
├── alembic.ini └── database └── migrations ├── README ├── env.py ├── script.py.mako └── versions
データベース情報の記述
例えばdockerでこのようなmysqlのDBコンテナを立てているとします。
docker run --name mysql -e MYSQL_ROOT_PASSWORD=mysql -p 3306:3306 -d mysql docker exec -it mysql bash root@# mysql -uroot -pmysql mysql> create database database;
マイグレーションを使ってDBを更新するための情報をalembic.ini
に記載します。
# format <driver>://<user>:<password>@<host>:<port>/<database> =>適宜環境に合わせる sqlalchemy.url = mysql+pymysql://root:mysql@127.0.0.1:3306/database?charset=utf8
もう一つ、これは任意ですがマイグレーションファイルの命名規則を変更します。
デフォルトはリビジョンが書かれていますが、それだと順序がわかりづらいので日付形式に変更します。
追加でalembic.ini
を変更します。
<変更前> file_template = %%(rev)s_%%(slug)s <変更後> file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d%%(second).2d_%%(slug)s
環境構築 〜SQLAlchemyを使ったモデルの作成〜
モデルの作成
ここでは以下のよくあるユーザーモデルと、そのユーザーが持つ権限を管理するテーブルを作ることにします。
* User * id * 名前 * メールアドレス * 郵便番号 * 住所 * ステータス * 作成日時 * 更新日時 * UserAuthorization * id * ユーザーID * 権限 * ステータス * 作成日時 * 更新日時
まずはapp/modelsにuser.pyとuser_authorization.pyいうファイルを作成します。
$ touch app/models/user.py $ touch app/models/user_authorization.py
作ったユーザーモデルのカラムデータを実装していきましょう!
User Model
from sqlalchemy import Column, BigInteger, Integer, String, DATETIME from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship Base = declarative_base() class User(Base): __tablename__ = "users" id = Column(BigInteger, primary_key=True, nullable=False) name = Column(String(255), nullable=False) email = Column(String(255), nullable=False) status = Column(Integer, server_default="1", nullable=False) created_at = Column(DATETIME, nullable=False) updated_at = Column(DATETIME, nullable=False) # Relationship user_authorizations = relationship("user_authorizations")
UserAuthorization Model
from sqlalchemy import Column, BigInteger, Integer, DATETIME, ForeignKey from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class UserAuthorization(Base): __tablename__ = "user_authorizations" id = Column(BigInteger, primary_key=True, nullable=False) user_id = Column(BigInteger, ForeignKey("users.id"), nullable=False) auth_type = Column(Integer, nullable=False) created_at = Column(DATETIME, nullable=False) updated_at = Column(DATETIME, nullable=False)
モデルはたったこれだけで完了です。
細かい仕様については次で説明していきます。
SQLAlchemyのモデルの書き方
SQLAlchemyの仕様について調べたものをまとめます。
データ型
(公式HP)によるとこれらのデータ型が用意されているようです。
BIGINT, BINARY, BIT, BLOB, BOOLEAN, CHAR, DATE, DATETIME, DECIMAL, DECIMAL, DOUBLE, ENUM, FLOAT, INTEGER, LONGBLOB, LONGTEXT, MEDIUMBLOB, MEDIUMINT, MEDIUMTEXT, NCHAR, NUMERIC, NVARCHAR, REAL, SET, SMALLINT, TEXT, TIME, TIMESTAMP, TINYBLOB, TINYINT, TINYTEXT, VARBINARY, VARCHAR, YEAR
使いたい型をsqlalchemyからインポートするようにしてください。
from sqlalchemy import Column, BigInteger, Integer, String, DATETIME
オプション設定
公式サイトはここに説明があります。
Operation Reference — Alembic 1.4.1 documentation
- Null許可設定
name = Column(String(255), nullable=False)
- デフォルト
status = Column(Integer, server_default="1", nullable=False)
- PK
id = Column(BigInteger, primary_key=True, nullable=False)
- FK
1:Nなどでリレーションをはる場合には、FK側に外部キーの設定をして
user_id = Column(BigInteger, ForeignKey("users.id"), nullable=False)
参照される側にはリレーションのテーブル名を記述します。
from sqlalchemy.orm import relationship (省略) user_authorizations = relationship("user_authorizations")
リレーションについては様々なパターンがあるので公式サイトを参照してください。
Basic Relationship Patterns — SQLAlchemy 1.3 Documentation
おそらくこれだけあれば、よくあるモデルのデータ構築はできるかと思います!
環境構築 〜Alembicでの複数モデルからのマイグレーション自動生成〜
ここでは複数のモデルファイルがあることを想定して、マイグレーションファイルを自動生成していきます。
複数モデルの読み込み
1つのモデルファイルを扱うだけなら公式ドキュメントで大丈夫ですが、複数のモデルファイルを扱うのは少々工夫が必要となります。
その際に参考にさせて頂いたサイトはこちらです。
alembic support multiple model files | Andrew's Blog
env.py
from logging.config import fileConfig from sqlalchemy import engine_from_config, pool, MetaData from alembic import context # ----------追加-------------- import importlib import os import sys sys.path.append(os.getcwd()) # ---------------------------- # 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) # ----------追加-------------- # 自動マイグレーション作成の対象モデル target_models = [ "app.models.user", "app.models.user_authorization", ] def import_model_bases(): lst = list(map(lambda x: importlib.import_module(x).Base.metadata, target_models)) return lst def combine_metadata(lst): m = MetaData() for metadata in lst: for t in metadata.tables.values(): t.tometadata(m) return m target_metadata = combine_metadata(import_model_bases()) # ----------------------------
以上で設定は完了です。
補足
- モデルファイルが増えたら下記にモデルファイル名のパスを追加
target_models = [ "app.models.user", "app.models.user_authorization", ]
- データ型変化の検知
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, ) with connectable.connect() as connection: context.configure( connection=connection, target_metadata=target_metadata, compare_type=True # 追加 )
マイグレーションの実行
やっと環境が整いました。では早速マイグレーションの自動生成をしてみましょう。
下記コマンドを打つと例のようなマイグレーションファイルが生成されます。
alembic revision --autogenerate -m "Create tables" # ex) => database/migrations/versions/f493fb95394b_create_tables.py
生成したマイグレーションファイルを実行してみましょう。
alembic upgrade head
お疲れ様です!これで作業は完了となります!
ちなみに下記コマンドでマイグレーション実行前に戻せます。
alembic downgrade −1
おまけ 〜ERAlchemyを使ったER図の自動生成〜
最後にSQLAlchemyと相性のいいERAlchemyを紹介して終わります。
ERAlchemyはDBからER図を生成してくれるライブラリです。
インストール
# grapghvizをbrewでローカルインストール brew install grapghviz # ERAlchemyのインストール pip install eralchemy
ER図の生成
インストールが完了したら下記コマンドを打ってみます。
# eralchemy -i <driver>://<user>:<password>@<host>:<port>/<database> -o <output filename> $ eralchemy -i mysql+pymysql://mysql:mysql@127.0.0.1:3306/mysql -o er-diagram.png
するとこんな感じでER図を生成してくれました。
こんなライブラリを使えばドキュメントのメンテも不要になるので、ぜひ使ってみてください!