概要
flask-migrate を使ってカラム名を変更すると既存のカラムの情報が新規のカラムに引き継がれません
原因はマイグレーションスクリプト内で drop_column
-> add_column
しているからです
今回はカラム名を変更してもデータをロストせずにマイグレートする方法を紹介します
環境
- macOS 11.1
- Python 3.8.5
- flask 1.1.2
- flask-migrate 2.5.3
事象の確認
まずは本当にカラム名を変更したら既存のカラムのデータが引き継がれないのか確認します
Flask アプリ
vim my_app/__init__.py
from flask import Flask
from flask_migrate import Migrate
from my_app.database import db
app = Flask(__name__)
app.debug = True
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+mysqldb://root:@localhost/test?charset=utf8"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SQLALCHEMY_ECHO'] = False
db.init_app(app)
migrate = Migrate(app, db)
database.py
vim my_app/database.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))
age = db.Column(db.Integer)
def __repr__(self):
return "%s,%s,%i" % (self.id, self.name, self.age)
データベース作成
mysql> create database test;
初期マイグレート
FLASK_APP=my_app pipenv run flask db init
FLASK_APP=my_app pipenv run flask db migrate -m "Initial migration."
FLASK_APP=my_app pipenv run flask db upgrade
作成されるテーブル情報は以下の通りです
テストデータ登録
mysql> insert into user values (null, 'hawk', 10);
mysql> insert into user values (null, 'snowlog', 20);
1mysql> insert into user values (null, 'hawksnowlog', 30);
テーブル構成変更
name カラムを first_name というカラム名に変更してみます
vim my_app/database.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(128))
age = db.Column(db.Integer)
def __repr__(self):
return "%s,%s,%i" % (self.id, self.name, self.age)
FLASK_APP=my_app pipenv run flask db migrate -m "A name column to first_name."
FLASK_APP=my_app pipenv run flask db upgrade
これで user テーブルを確認すると first_name
カラムのデータが NULL になってしまっているのが確認できます
対策
flask db migarte
時に自動生成されるマイグレートスクリプトを修正することで対応してみます
マイグレートスクリプトの編集
まずは migrate するところまで進めましょう
upgrade までしてしまうと適用されてしまうので upgrade はまだしません
FLASK_APP=my_app pipenv run flask db migrate -m "A name column to first_name."
そして生成されるマイグレートスクリプトを編集します
リビジョン番号はそのたびに違うので自信の環境に併せて書き換えてください
upgrade と downgrade 関数を以下のように書き換えます
ポイントは alter_column
という関数を使ってカラムの情報を書き換えるようにします
また existing_type
と type_
を指定しましょう
前者は変更前のカラムのタイプで後者は変更後のカラムのタイプになります
今回はタイプは変更しないので同じものを指定しています
* vim migrations/versions/be9a17e479a6_a_name_column_to_first_name.py
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('user', 'name', nullable=True, new_column_name='first_name', type_=sa.String(128), existing_type=sa.String(128))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('user', 'first_name', nullable=True, new_column_name='name', type_=sa.String(128), existing_type=sa.String(128))
# ### end Alembic commands ###
動作確認: upgrade してみる
書き換えたマイグレートスクリプトを使って upgrade してみます
FLASK_APP=my_app pipenv run flask db upgrade
これでテーブルを確認するとちゃんと前のデータが引き継がれていることが確認できると思います
また upgrade 後のテーブル情報は以下のようになっています
最後に
flask-migrate を愚直に使うと drop_column
してデータをロストしかねないのでマイグレートするときは必ず alter_column
を使うようにしましょう
0 件のコメント:
コメントを投稿