おけらのブログ++

駆け出しWebエンジニアの奮闘記

SQLAlchemy+Alembic:モデルからマイグレーションファイルを自動生成

目次

  1. この記事について
  2. SQLAlchemyとAlembic
  3. 環境構築 〜インストールと設定〜
  4. 環境構築 〜SQLAlchemyを使ったモデルの作成〜
  5. 環境構築 〜Alembicでの複数モデルからのマイグレーション自動生成〜
  6. おまけ  〜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の特徴

f:id:Okerra:20200129011006j:plain

  • Python の ORM
  • エンジンとしてDBの接続処理を管理できる
  • 様々なDBを同一のソースコードで管理できる

要は直接SQL文を書くことなくSQLiteMySQLPostgreSQL 等のデータベースを操作することができます。

docs.sqlalchemy.org

Alembicの特徴

ちょっと前まではsqlalchemy-migrationというライブラリだったようです。

ただすでに開発はストップしており、後継者としてAlembicが開発されているようです。

環境構築〜インストールと設定〜

f:id:Okerra:20200129012155p:plain さぁ、それでは頑張って環境構築していきましょう!!

まずはライブラリのインストールと設定をしていきます。

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を使ったモデルの作成〜

f:id:Okerra:20200129012528p:plain

モデルの作成

ここでは以下のよくあるユーザーモデルと、そのユーザーが持つ権限を管理するテーブルを作ることにします。

* 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での複数モデルからのマイグレーション自動生成〜

f:id:Okerra:20200205095830p:plain ここでは複数のモデルファイルがあることを想定して、マイグレーションファイルを自動生成していきます。

複数モデルの読み込み

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を紹介して終わります。

github.com

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図を生成してくれました。

f:id:Okerra:20200205102012p:plain

こんなライブラリを使えばドキュメントのメンテも不要になるので、ぜひ使ってみてください!