概要
Flask + SQLAlchemy を使う場合には専用のライブラリがあります
データベースに変更を加える際のマイグレーションも専用のライブラリが用意されているので使ってみました
環境
- macOS 10.15.5
- MySQL 8.0.19
- Python 3.8.3
- Flask-Migrate 2.5.3
MySQL 準備
今回は Homebrew で最新版をインストールします
最新版だと Python の mysqlclient がエラーになりますがトラブルシューティングに記載の方法で修正できるので最新版を使います
brew install mysql
brew services start mysql
また事前にデータベースを作成しておきましょう
ユーザやパスワードはデフォルトのものを使います
mysql -u root -e "createdb test;"
ライブラリインストール
必要なライブラリをインストールしましょう
今回は MySQL を使うのでドライバとして mysqlclient
を追加でインストールします
pipenv install Flask-Migrate mysqlclient
Flask アプリの作成
マイグレーションするには Flask のアプリが必要になります
簡単なアプリで良いので作成しましょう
vim my_app/__init__.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
マイグレーション定義の追加
作成した Flask アプリにマイグレーションの定義を追加します
本当は別モジュールで管理したいところですが Flask-Migrate の仕様上そのままアプリに定義を追加します
from flask import Flask
app = Flask(__name__)
# migrate
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+mysqldb://{}:{}@{}/{}?charset=utf8".format("root", "", "localhost", "test")
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
migrate = Migrate(app, db)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))
@app.route('/')
def hello_world():
return 'Hello, World!'
マイグレーションするテーブル定義は db.Model
を継承したクラスを作成することで行います
このあたりは SQLAlchemy の使い方をそのまま流用できます
また Migrate
クラスのオブジェクト (migrate) をこのファイル内のどこかで必ず生成する必要があります
これが生成されていないとトラブルシューティングにある KeyError が発生します
マイグレーションリポジトリの初期化
まずはマイグレーションするための専用のリポジトリを作成する必要があります
実行時に FLASK アプリが存在しているモジュール名を指定しましょう
あとは flask db init
コマンドを実行すれば OK です
FLASK_APP=my_app pipenv run flask db init
成功すると migrations
ディレクトリ配下にマイグレーションに必要なファイルが作成されます
どうやら alembic というマイグレーションライブラリの仕組みを使っているようです
マイグレーションスクリプトの作成
次にマイグレーションするためのスクリプトを作成します
この段階ではまだテーブルなどの作成、変更はされません
コマンドは flask db migrate
になります (コマンドを見るとマイグレーションされそうですがされません)
-m
を使ってマイグレーション時のコメントを残すことができます
コメントは履歴に残るのでどういったマイグレーションだったのかわかりやすいコメントを残しましょう
FLASK_APP=my_app pipenv run flask db migrate -m "Initial migration."
FLASK_APP=my_app pipenv run flask db history
<base> -> 2367123248c0 (head), Initial migration.
以下のように成功すれば OK です
migrations/versions/2367123248c0_initial_migration.py
がマイグレーションに使用されたスクリプトになるので実際に実行されるコマンドを確認したい場合はこのファイルを確認しましょう
アップグレードしてみる
では実際にテーブルを作成してみます
コマンドは flask db upgrade
になります
FLASK_APP=my_app pipenv run flask db upgrade
実行されたマイグレーションスクリプトが先程作成されたスクリプトであることが確認できます
これで実際に MySQL を覗いてみるとテーブルが作成されているのが確認できると思います
mysql -u root test -e "desc user;"
データベースの構成を変更してみる
とりあえずテーブルを作成することはできました
今度はカラムを一つ追加するマイグレーションをしてみます
vim my_app/__init__.py
from flask import Flask
app = Flask(__name__)
# migrate
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+mysqldb://{}:{}@{}/{}?charset=utf8".format("root", "", "localhost", "test")
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
migrate = Migrate(app, db)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))
age = db.Column(db.Integer) # <- New
@app.route('/')
def hello_world():
return 'Hello, World!'
こんな感じでカラムを一つ追加してみましょう
そして migrate -> upgrade してみます
FLASK_APP=my_app pipenv run flask db migrate -m "Add a new column."
FLASK_APP=my_app pipenv run flask db upgrade
先ほどと同様にマイグレーションスクリプトが作成されてそれが実行されたのが確認できると思います
MySQL を直接確認してもわかると思います
なお history でもちゃんとマイグレーションの履歴が追加されているのが確認できると思います
2367123248c0 -> 1f939fcbbde7 (head), Add a new column.
<base> -> 2367123248c0, Initial migration.
ダウングレードもできる
ちなみにロールバックするコマンドもあり downgrade を使います
FLASK_APP=my_app pipenv run flask db downgrade
この場合 history は変わらないので注意しましょう
現在どこまでマイグレーションスクリプトが適用されているか確認するのは current
コマンドを使います
FLASK_APP=my_app pipenv run flask db current
heads や show コマンドはマイグレーションスクリプトの現在の最新を確認するコマンドなのでごっちゃにならないようにしましょう
おまけ: Python スクリプトとしても実行できる
今回は flask コマンドのサブコマンドとして実行しましたが普通に python コマンドからでも実行できます
Flask-Script というライブラリが追加で必要になるのでインストールします
pipenv install Flask-Script
スクリプトは Flask アプリのモジュールが参照できるパスに作成する必要があります
vim my_app/__init__.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
マイグレーション用のスクリプトは少し変わります
先程インストールした Flask-Script の Manager クラスを使います
またスクリプトとして実行できるように main を追加します
vim manager.py
from my_app import app
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+mysqldb://{}:{}@{}/{}?charset=utf8".format("root", "", "localhost", "test")
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))
age = db.Column(db.Integer)
if __name__ == '__main__':
manager.run()
あとは init -> migrate -> upgrade の流れを実行すれば OK です
実行するときは作成した manager.py
を python コマンドで実行します
FLASK_APP=my_app pipenv run python manager.py db init
FLASK_APP=my_app pipenv run python manager.py db migrate -m "Initial migration."
FLASK_APP=my_app pipenv run python manager.py db upgrade
またスクリプト版をテストする場合は MySQL 側のデータも作成おきましょう
mysql -u root test -e "drop table alembic_version;"
mysql -u root test -e "drop table user;"
最後に
Flask + Flask-Migrate を使って MySQL のマイグレート管理をしてみました
ディレクトリとファイルの構成にルールがあるのでそこだけ注意しましょう
これでデータベースのマイグレーションの変更履歴もちゃんと git で管理できるようになります
トラブルシューティング
KeyError: 'migrate'
が出る場合
マイグレーションする場合は FLASK_APP
で指定したアプリのモジュール配下にある __init__.py
を使います
そこに Migrate オブジェクトの migarate
が記載されていることを想定しているので別ファイルや別モジュールにマイグレーションの定義をしている場合は KeyError になるようです
ファイルの構成やディレクトリの構成を見直すのとちゃんと __init__.py
に migrate オブジェクトが存在するか確認しましょう
Library not loaded: /usr/local/opt/mysql/lib/libmysqlclient.20.dylib
おそらく Mac の環境でなければでないと思います
今回使用した MySQL のバージョンは最新の 8.0.19 になります
その場合はクライアント用のライブラリが /usr/local/opt/mysql/lib/libmysqlclient.21.dylib
になるようで Python の mysqlclient ライブラリだとまだそれを使用していないようでエラーになります
一番てっとり早いのは libmysqlclient.20.dylib
のシンボリックリンクを作成すれば解決するはずです
cd /usr/local/opt/mysql/lib
ln -s libmysqlclient.dylib libmysqlclient.20.dylib
0 件のコメント:
コメントを投稿