今回はFlask-Migrateの使え方についてメモします。まずFlask-Migrateはなんなのか公式ドキュメントをみてみると…
Flask-Migrate is an extension that handles SQLAlchemy database migrations for Flask applications using Alembic. The database operations are made available through the Flask command-line interface or through the Flask-Script extension.
うんうん、いわゆるFlaskのExtensionの一つでSQLAlchemyのデータベースをマイグレーションを簡単にできるってことですね〜あとこの操作はFlaskのコマンドラインかFlask-Script Extensionかのことかな。まずFlask-Migrateをインストールしましょう。
pip install Flask-Migrate
次はFile構成:
- Web
- Manger.py
いまのFile構成はこんな感じですね、WebのFolderの下にManger.pyがあります。Manger.pyはどんなコードが入ってるかどういうど…
from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_script import Manager from flask_migrate import Migrate,MigrateCommand app=Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db' db=SQLAlchemy(app) migrate=Migrate(app,db) manager=Manager(app) manager.add_command('db',MigrateCommand) class Person(db.Model): id=db.Column(db.Integer,primary_key=True) name=db.Column(db.String(20)) email=db.Column(db.String(120)) address=db.Column(db.String(120),nullable=False) tel=db.Column(db.String(20),nullable=False) password=db.Column(db.String(60),nullable=False) spare1=db.Column(db.String(21)) if __name__=='__main__': manager.run()
いまはFolderがDBさえ入ってはい状態でManger.pyを走ってみよう。
python Manger.py db init
Terminalでいろいろなメッセージが出てきて、終わったらFile構成がこうなります。
- Web
- Manger.py
- migrations←
migrationsというFolderが増えてきました。じゃ次はのコマンドを走りましょう。
python Manger.py db migrate INFO [alembic.runtime.migration] Context impl SQLiteImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.autogenerate.compare] Detected added table 'person' Generating /Users/chrischung/Google Drive/Web/migrations/versions/c1cc8fa0d2c6_.py ... done
って感じですね。ほ、私が新しいTableを追加してたと気ついてたね〜そしてmigrations/versions/の中に一つのpyスクリプトが追加された。じゃこのc1cc8fa0d2c6.pyがなにが入ってるですか?
"""empty message Revision ID: c1cc8fa0d2c6 Revises: Create Date: 2018-07-24 05:30:05.947666 """ from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = 'c1cc8fa0d2c6' down_revision = None branch_labels = None depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### op.create_table('person', sa.Column('id', sa.Integer(), nullable=False), sa.Column('name', sa.String(length=20), nullable=True), sa.Column('email', sa.String(length=120), nullable=True), sa.Column('address', sa.String(length=120), nullable=False), sa.Column('tel', sa.String(length=20), nullable=False), sa.Column('password', sa.String(length=60), nullable=False), sa.Column('spare1', sa.String(length=21), nullable=True), sa.PrimaryKeyConstraint('id') ) # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### op.drop_table('person') # ### end Alembic commands ###
Manger.pyの中に定義されたClassがここでDBに入れるってわけですね。じゃ最後のコマンドを走りましょう。
python Manger.py db upgrade INFO [alembic.runtime.migration] Context impl SQLiteImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.runtime.migration] Running upgrade -> c1cc8fa0d2c6, empty message
- Web
- Manger.py
- migrations
- test.db←
おお、DBが作成されました!じゃ、最後はsqlite3コマンドで確認しましょうか。
sqlite3 test.db sqlite>.schema CREATE TABLE person ( id INTEGER NOT NULL, name VARCHAR(20), email VARCHAR(120), address VARCHAR(120) NOT NULL, tel VARCHAR(20) NOT NULL, password VARCHAR(60) NOT NULL, spare1 VARCHAR(21), PRIMARY KEY (id) );
OK、それで大丈夫だね。次はManger.pyにもうひとつのTableを追加しましょう。
class tempClass(db.Model): id=db.Column(db.Integer,primary_key=True) title=db.Column(db.String(20))
そしてもう一度コマンドを走ります。
python Manger.py db migrate INFO [alembic.runtime.migration] Context impl SQLiteImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.autogenerate.compare] Detected added table 'temp_clasee' Generating /Users/chrischung/Google Drive/Web/migrations/versions/6c77edaef8c4_.py ... done python Manger.py db upgrade INFO [alembic.runtime.migration] Context impl SQLiteImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.runtime.migration] Running upgrade c1cc8fa0d2c6 -> 6c77edaef8c4, empty message
どうやらうまくいけそうですね。
sqlite3 test.db sqlite>.schema CREATE TABLE person ( id INTEGER NOT NULL, name VARCHAR(20), email VARCHAR(120), address VARCHAR(120) NOT NULL, tel VARCHAR(20) NOT NULL, password VARCHAR(60) NOT NULL, spare1 VARCHAR(21), PRIMARY KEY (id) ); CREATE TABLE temp_clasee ( id INTEGER NOT NULL, title VARCHAR(20), PRIMARY KEY (id) );
うん、それは大丈夫だね!
実はいくつの問題がありますが…
- Tableの内容が修正されてMigrateしてもなにも変わらない。
- ネットでみたらどうやら100%修正したものをDetectできるわけでもないらしいのでmigrateのコマンド走ったあとは必ずそのとき作成されたpyを一回みること。
- nullable=Falseのコラムがupgradeコマンド走るときはエラー出る問題
- うん…これはわかりません。いまはとりあえずnullableを入れないで、DBがマイグレーション終わったら手動でSqliteコマンドでCOPYー>修正ー>貼り付けって感じですね。参考Link:https://stackoverflow.com/questions/29583396/modify-sqlite-table-column-not-null-to-null
それじゃねー