Flask-Migrateを使ってカラム情報を変更する

上記記事で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テーブルのcreatedcreated_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

One thought on “Flask-Migrateを使ってカラム情報を変更する”

コメントを残す

メールアドレスが公開されることはありません。