Contents
上記記事でFlaskチュートリアルの公式のテーブル設計からどのように変更していったか記載していきます。
公式のテーブル設計 はイケてないので若干内容を変更しています。
スポンサードサーチ
カラム名変更1
postsテーブルに作成者を扱うauthor_id
カラムがあるが、こはusers.id
との外部参照キーなのでuser_id
の方が直感的である。
『基本』は モデルファイルを編集 → flask db migrate 実行の順
になるのだが、カラム名変更においては例外となる
なぜなら、モデルを以下のようにカラム名部分のみを編集して、flask db migrate
を実行すると、
- author_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
+ user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
以下のようなrevisionファイルが生成される。
def upgrade():
op.create_foreign_key(None, 'posts', 'users', ['user_id'], ['id'])
op.drop_column('posts', 'author_id')
def downgrade():
op.add_column('posts', sa.Column('author_id', mysql.INTEGER(), autoincrement=False, nullable=False))
op.drop_constraint(None, 'posts', type_='foreignkey')
これだと、
カラム名をauthor_id → user_id への変更ではなく
user_idカラムを新設し、author_idを削除することになる。
それの何が問題かと言うと、
すでにレコードが存在する場合、author_idは外部参照しているのでそもそも消せないからだ。
NOT NULL 制約があるカラムも同様に行えない。
(この辺りのDBの仕様に関してはここでは触れません)
では、どうすればいいのか?
1: 空revisionファイルの生成
まず、下記コマンドでモデルを編集することなく、revisionファイルを生成します。
$ flask db revision
"""empty message
Revision ID: xxxxxxxxxxxx
Revises: xxxxxxxxxxxx
Create Date: 2020-xx-xx xx:xx:xx.xxxxxx
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'xxxxxxxxxxxx'
down_revision = 'xxxxxxxxxxxx'
branch_labels = None
depends_on = None
def upgrade():
pass
def downgrade():
pass
※ 空revisionファイル
という呼称は私が勝手にそう呼んでいます。
正式名称はわかりません。。。 (きちんと調べてもいないです m(_ _)m
2: revisionファイルの編集
上記で生成したrevisionファイルの upgrade()
、downgrage()
を編集します。
(書き方に関してはAlembic’s documentation > Operation Reference を参照)
def upgrade():
- pass
+ op.alter_column(table_name='posts', column_name='author_id', existing_type=sa.Integer, new_column_name='user_id')
def downgrade():
- pass
+ op.alter_column(table_name='posts', column_name='user_id', existing_type=sa.Integer, new_column_name='author_id')
3: migration実行
以下のコマンドでmigrationを実行します。
これでカラム名変更が実行されます。
$ flask db upgrade
4: モデルの編集
後は、DBの内容とモデルの内容を揃えるためにモデルを編集するだけです。
(モデル以外で呼び出しているところがあれば変更を忘れずに)
- author_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
+ user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
これでよし!っと思いましたが、外部参照キーなのでindexも変更が必要です。
index名も変更しましょう。
index名変更
名称変更系はカラムでもindexでもやることは一緒です。
空revisionファイルを生成し、以下のようにします。new_index_name
のような便利なものは見つけられなかったので、exectute
でsql文を直書きしてみました。
(indexだったら、drop_index()後にcreate_index()でもよいかも)
def upgrade():
- pass
+ op.execute("ALTER TABLE posts RENAME INDEX author_id to user_id")
def downgrade():
- pass
+ op.execute("ALTER TABLE posts RENAME INDEX user_id to author_id")
後は、同様にmigrationを実行すればOK
カラム名変更2
postsテーブルのcreated
を created_at
に変更したい。
空revisionファイルを生成し、以下のようにします。
def upgrade():
- pass
+ op.alter_column(table_name='posts', column_name='created', existing_type=sa.DateTime, new_column_name='created_at')
def downgrade():
- pass
+ op.alter_column(table_name='posts', column_name='created_at', existing_type=sa.DateTime, new_column_name='created')
後は、同様にmigrationを実行すればOK
カラム追加1
usersテーブルにcreated_at
を追加したい。
カラム追加は基本に沿って、 モデルファイルを編集 → flask db migrate 実行の順
でOK。
1: モデル編集
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), unique=True, nullable=False)
password = db.Column(db.Text, nullable=False)
+ created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
2: revisionファイル生成
$ flask db migrate
def upgrade():
op.add_column('users', sa.Column('created_at', sa.DateTime(), nullable=False))
def downgrade():
op.drop_column('users', 'created_at')
3: migration実行
$ flask db upgrade
これでカラム追加完了。。。とはなりませんでした!!!
なぜかといいますと、
モデルでは nullable=False, default=datetime.utcnow
にしたにも関わらず
revisionではdefaultが自動生成されないようです?
そのため、add_column にserver_default
を追記します。
def upgrade():
op.add_column('users', sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.func.now()))
こで一通り、自分好みのテーブル構成になりました。
以上です!
参考
revisionファイルの書き方:
Alembic’s documentation > Operation Reference
Flask-Migrateを使ってのカラム名変更:
stackoverflow > flask-migrate-and-changing-column-type のAnswer
Alembic does not recognize things like column type changes when auto-generating revisions by default. When making these more granular changes, you will need to hand-modify the migration to include these changes
Thank you!!1