12
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

PythonAdvent Calendar 2021

Day 12

FlaskでのMigrationって結局どう運用するのが正しいのか

Last updated at Posted at 2021-12-12

はじめに

Flaskでデータベースを扱う場合、OR MapperとしてSQLAlchemyがまだ今の所デファクトスタンダードだと思われる。
個人的にはあまりライブラリ依存したくないのでFlask-SQLAlchemyを使わずSQLAlchemyをそのまま使うようにしている(WTformsとかもね)

ただそういうこだわりをもっていて何か大切なものを見失っては困るので大分前だがとあるプロジェクトにFlask-SQLAlchemyを導入した。
そうなるとFlask-Migrateにもお世話になるのが筋だと思いこちらも使ってみた。

Flask-Migrateについてはよく理解していないとハマる。
実はつい今しがたもそのプロジェクトのテスト環境を久々に作ろうとしてツボったのだ。
そのため備忘録というか少しでも参考になればと思い、私なりの使い方を紹介する。

基本的な使い方

一番最初はこの三点セット

flask db init    # マイグレーションのリポジトリ初期化(git initみたいなもん)
flask db migrate # マイグレーション用のスクリプトを生成(migrations/versions/配下のxxx_.py)
flask db upgrade # データベースへ書き込み

実際は FLASK_APP=run:app flask db init みたいに環境変数が必要

後はモデルの修正するたびに

flask db migrate
flask db upgrade

ってやればよい。リビジョン管理がされているのでロールバックというかダウングレードもできる。

flask db downgrade

以上、終了。

でだいたいどこに書いてある記事でもこんな説明になっている。
悩ましいのは生成されるmigrationsをgitの管理下に置くかということだ。

migrationsディレクトリって環境毎につくるもの?

恐らくこれが正解だと思う。

Flaskアプリの環境とDBが分離していればよほど問題にならない。
私の場合Dockerのコンテナに両方を入れているので新しい環境にデプロイした場合データベースの同期をしない限り新しいデータベースが作成される。
そうすると新規でデータベースを作る時に次のような問題が生じる。

モデルの変更が一度もない場合だとflask db migrate && flask db upgradeですんなりテーブル作成できる。
そうでない場合、flask db migrateは更新の差分情報しかもっていないのでflask db upgradeでこける。

こんなふうにしてみた

新しい環境にアプリを移行してデータベースを初期化する場合は以下のような回りくどい手順を実行すればなんとかなる。

現在のリビジョンを調べる

元となるmigrationsのリビジョンを調べる。
またはmigrations/versions/配下のファイルを漁るかmysqlなら

mysql> select * from alembic_version
+--------------+
| version_num  |
+--------------+
| abcdefghijkl |
+--------------+

最新リビジョンはabcdefghijklだとわかる。

flask db history というコマンドもあるようだが使ったこと無いので未確認

新規migrationsを作成する

次に新しい環境にデプロイされたmigrationsディレクトリを一旦退避する。

mv migrations migrations.bak

新しい環境で初期化の三点セットを実行

flask db init
flask db migrate
flask db upgrade

ここで発行されるリビジョンがxxxxxxxxxxだとする。

元のリビジョン番号に戻す

migrationsを元に戻す。

mv migrations migrations.new
mv migrations.bak migrations

最後にテーブルを書き換える

update into alembic_version set version_num='abcdefghijkl' where version_num='xxxxxxxxxxxx'

以上。

とここまで書いたが。。。
自分の書いたREADMEを読み直してみると

新しい環境でデータベースを初期化したい場合はきっとこちらが正しい

最初にユーザとデータベースの作成

GRANT ALL PRIVILEGES ON *.* TO foo@"%" IDENTIFIED BY 'foo' WITH GRANT OPTION;
CREATE DATABASE mysite;

モデルからテーブルを作成

FLASK_APP=run:app flask shell
>>> from app.database import db
>>> db.create_all()
>>> db.session.commit()

マイグレーション実行

FLASK_APP=run:app flask db migrate
FLASK_APP=run:app flask db stamp head
FLASK_APP=run:app flask db upgrade

参考

run.py

import os

from app import create_app

config = os.getenv('FLASK_ENV', 'production')
app = create_app(config)

if __name__=='__main__':
    app.run(host='0.0.0.0', debug=True)

config.py(抜粋)

# 省略

class Config(object):
    ## Default constants
    DEBUG = False
    TESTING = False
    DEVELOPMENT = False

    # 省略

class ProductionConfig(Config):
    pass

class StagingConfig(Config):
    DEVELOPMENT = True

class DevelopmentConfig(Config):
    DEBUG = True
    DEVELOPMENT = True

class TestingConfig(Config):
    DEBUG = True
    TESTING = True

app_config = {
    'production': ProductionConfig,
    'staging': StagingConfig,
    'development': DevelopmentConfig,
    'testing':  TestingConfig,
}

app/__init__.py(抜粋)

# 省略
from flask import Flask

from config import app_config


def create_app(config, local_config='config.py'):
    # create flask instance
    app = Flask(__name__, instance_relative_config=True)
    app.config.from_object(app_config[config])

    # 省略

    return app

app/database.py

from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

db = SQLAlchemy()

def init_db(app, migrate_dir=None):
    db.init_app(app)
    Migrate(app, db, migrate_dir) if migrate_dir else Migrate(app, db)

とある。うっ汗。
いやまあ手動でリビジョン直したい場合がどこかであるかもってことで。。。(いや、実際にどこかであったぞ)

12
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?