58:DBのスキーママイグレーションとデータマイグレーションを分ける

プログラミング迷子: トラブルに弱いマイグレーション実装

  • 後輩W:Djangoでモデルにフィールドを追加したので マイグレーション したところ、自分の環境とCIでは問題なかったんですが、開発サーバーで実行したら途中でエラーになって直せなくなってしまいました。こういう場合、テーブルを直接直したりしていいんでしょうか?

  • 先輩T:途中でエラーっていうと、どんなエラー?

  • 後輩W:追加しようとしたカラムがNULL不可で、データがある場合にデフォルト値がないせいでエラーになりました。

  • 先輩T:Djangoのmigrateコマンドならバージョン指定することで ロールバック できると思うけど、やってみた?

  • 後輩W:それが、データを移動する処理も同じmigrationコードに書いていて、ロールバックしようとするともう必要なカラムがなくてエラーになります。

  • 先輩T:なるほど、 スキーママイグレーション と データマイグレーション を1回でやろうとしたのか。MySQLではスキーマ変更に トランザクション が効かないから、エラーが起きた時点の状態で確定されちゃうんだよね。だからテーブルを直接直すしかなさそう。次からはデータマイグレーションを別のバージョンに分けると良いね。

DjangoのORM(Object-Relational-Mapping)はマイグレーション機能も提供しています 1 。 Django ORMのモデルに定義したフィールドの移動は、実際にはフィールドの削除と新規追加として扱われます。このような変更に対するマイグレーションファイルは、1つのマイグレーションでカラムの追加と削除を行います。

1

『Pythonプロフェッショナルプログラミング第3版』(ビープラウド著、秀和システム刊、2018年6月)の14章でDjangoのマイグレーション機能について紹介しています。

cover

(中略)詳細は書籍 自走プログラマー をご参照ください

上記のマイグレーションでも基本的には問題ありませんが、もしエラーが発生したら困ることになります。 このマイグレーションを実行したとき、「カラム追加」が成功したあと「データマイグレーション」や「カラム削除」で失敗する可能性があります。 原因は、データマイグレーションコードの考慮不足かもしれませんし、カラム削除がローカル環境のSQLiteでうまくいっても本番環境のMySQLやPostgreSQLでうまくいかないケースなのかもしれません。

このような失敗が起こると、再実行も ロールバック もできなくなってしまいます。 マイグレーションの再実行は、すでに追加済みのカラムをさらに追加しようとして失敗します。 ロールバックは、最後の「カラム削除」をロールバックとして「カラム追加」しようとしますが、実際にはカラムはまだ削除されていないため、存在するカラムをさらに追加しようとして失敗します。

ベストプラクティス

スキーママイグレーションとデータマイグレーションは個別に実行できるように用意しましょう。

モデルのフィールドを別のモデルに移動する場合は、3回のマイグレーションに分けます。

cover

(中略)詳細は書籍 自走プログラマー をご参照ください