=============================================================== 58:DBのスキーママイグレーションとデータマイグレーションを分ける =============================================================== .. maigo:: トラブルに弱いマイグレーション実装 * 後輩W:Djangoでモデルにフィールドを追加したので :index:`マイグレーション` したところ、自分の環境とCIでは問題なかったんですが、開発サーバーで実行したら途中でエラーになって直せなくなってしまいました。こういう場合、テーブルを直接直したりしていいんでしょうか? * 先輩T:途中でエラーっていうと、どんなエラー? * 後輩W:追加しようとしたカラムがNULL不可で、データがある場合にデフォルト値がないせいでエラーになりました。 * 先輩T:Djangoのmigrateコマンドならバージョン指定することで :index:`ロールバック` できると思うけど、やってみた? * 後輩W:それが、データを移動する処理も同じmigrationコードに書いていて、ロールバックしようとするともう必要なカラムがなくてエラーになります。 * 先輩T:なるほど、 :index:`スキーママイグレーション` と :index:`データマイグレーション` を1回でやろうとしたのか。MySQLではスキーマ変更に :index:`トランザクション` が効かないから、エラーが起きた時点の状態で確定されちゃうんだよね。だからテーブルを直接直すしかなさそう。次からはデータマイグレーションを別のバージョンに分けると良いね。 DjangoのORM(Object-Relational-Mapping)はマイグレーション機能も提供しています [#pypro3django]_ 。 Django ORMのモデルに定義したフィールドの移動は、実際にはフィールドの削除と新規追加として扱われます。このような変更に対するマイグレーションファイルは、1つのマイグレーションでカラムの追加と削除を行います。 .. [#pypro3django] 『Pythonプロフェッショナルプログラミング第3版』(ビープラウド著、秀和システム刊、2018年6月)の14章でDjangoのマイグレーション機能について紹介しています。 .. omission:: 上記のマイグレーションでも基本的には問題ありませんが、もしエラーが発生したら困ることになります。 このマイグレーションを実行したとき、「カラム追加」が成功したあと「データマイグレーション」や「カラム削除」で失敗する可能性があります。 原因は、データマイグレーションコードの考慮不足かもしれませんし、カラム削除がローカル環境のSQLiteでうまくいっても本番環境のMySQLやPostgreSQLでうまくいかないケースなのかもしれません。 このような失敗が起こると、再実行も :index:`ロールバック` もできなくなってしまいます。 マイグレーションの再実行は、すでに追加済みのカラムをさらに追加しようとして失敗します。 ロールバックは、最後の「カラム削除」をロールバックとして「カラム追加」しようとしますが、実際にはカラムはまだ削除されていないため、存在するカラムをさらに追加しようとして失敗します。 ベストプラクティス ================== スキーママイグレーションとデータマイグレーションは個別に実行できるように用意しましょう。 .. index:: manage.py makemigrations モデルのフィールドを別のモデルに移動する場合は、3回のマイグレーションに分けます。 .. omission:: 関連 ==== * :doc:`59-データマイグレーションはロールバックも実装する`