FastAPI で RDB のマイグレーションを管理する

はじめに

個人開発で FastAPI を触る機会があり、Python すらほとんど書いたことがなかったので『動かして学ぶ!Python FastAPI開発入門』という書籍を読んでみた。コンパクトによくまとまっていて、FastAPI を使った開発の雰囲気を一通り掴むことができた。

RDBスキーママイグレーションの管理方法については記載がなかったので調べたところ、 ORM として sqlalchemy を使う場合、マイグレーション管理には alembic というライブラリを使うのが定番らしい。この記事では sqlalchemy と alembic、MySQL の組み合わせで、最低限のマイグレーション管理をする方法を考えてみる。

これ以降『動かして学ぶ!Python FastAPI開発入門』で説明されている実装が一通り終わっていることを前提として書いていく1。各ツールは以下のバージョンで動作確認を行った。

  • python 3.11.4
  • fastapi 0.111.0
  • sqlalchemy 2.0.31
  • alembic 1.13.1
  • mysql 8.0.37

alembic のインストール

pyproject.tomlalembic を追加する。

[tool.poetry.dependencies]
...
alembic = "1.13.1"
...

続いて次のコマンドを実行して alembic のパッケージをインストールする。

$ poetry install

alembic の初期設定

以下を実行してマイグレーション用のディレクトリ構成を初期化する(migrations の名前は任意)。

$ alembic init migrations

以下のディレクトリとファイルが生成される。

./
├ migrations
│    ├ README
│    ├ env.py
│    ├ script.py.mako
│    └ migrations/
...

続いてアプリケーショに合わせて migrations/env.py を修正する。

まずはモデル定義ファイルのメタデータを指定する。またモデル定義ファイルも import しておく必要があるらしい。

...
from api.db import Base
target_metadata = Base.metadata

import api.models.task # noqa: F401
...

次に sqlalchemy.url を設定する。

...
DB_URL = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
config.set_main_option("sqlalchemy.url", DB_URL)
...

alebic.init にも sqlalchemy.url の設定項目があるが、重複するのでこちらは削除しておく。

マイグレーションの実行

まずはモデルが定義された Python のファイルを編集する。例えば api/models/task.py で以下のように User モデルを追加する。

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    name = Column(String(1024), nullable=True)

以下のコマンドを実行すると、データベース側のスキーマ情報とモデルの定義を比較されて差分が versions ディレクトリ以下にマイグレーションファイルとして作成される。

$ alembic revision --autogenerate

今回のようにモデルを新しく追加した場合は、テーブル作成としてマイグレーションファイルが作成される。

...
def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('users',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=1024), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    # ### end Alembic commands ###


def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('users')
    # ### end Alembic commands ###
...

作成されたマイグレーションファイルを適用するには以下のコマンドを実行され、データベースのalembic_version テーブルに適用されたマイグレーションファイルの ID が追加される。

$ alembic upgrade head

また実際に発行される DDL--sql オプションで事前に確認することもできる。

$ alembic upgrade head --sql

おわりに

この記事では alembic を使った MySQLマイグレーション管理方法について説明した。基本的な流れとしては Python のモデル定義を修正してマイグレーションファイルを自動生成し、それを適用することを繰り返して行けば最低限の管理はできそうなことがわかった。

『動かして学ぶ!Python FastAPI開発入門』 では Cloud Run 上にアプリケーションをデプロイしていたが、その構成に合わせるのであればマイグレーションの実行は Cloud Run Jobs に切り出して実行するのが良さそう2


  1. ただし筆者の都合で Chapter 13 は飛ばしているので aiomysql 周りについては実装していない。
  2. Railsコマンドの実行環境をCloud Run Jobsに移行しました の記事が参考になりそう。