エラーの内容
makemigrationsまでは通るのですが、その後migrateしようとすると、以下のようなエラーに遭遇するときがあります。
1 |
The row in table 'app_fuga' with primary key '*' has an invalid foreign key: app_fuga.hoge_id contains a value '*' that does not have a corresponding value in app_hoge.id. |
このエラーは、開発環境でモデルを追加したり変更したりmigrateしたりを繰り返していると出ます。
直訳すると、
1 |
主キー「*」を持つテーブル「app_fuga」の行に無効な外部キーがあります:app_fuga.hoge_idには、app_hoge.idに対応する値がない値「*」が含まれています。 |
となり、つまり「子テーブル(app_fuga)の中に、親テーブル(app_hoge)の外部キーらしきIDの値があるけど、そんなIDを持つレコードはapp_hogeにはねーよ!」ということらしいです。
ここでは、app_hoge が親テーブル、app_fuga が子テーブルなので、app_fuga の hoge_id というカラムが、親テーブルのIDを参照する外部キーです。
このカラムに例えば「1」という値が入っていたとすると、app_hogeの中に、IDが「1」のレコードは存在しないと言っていることになります。
DBの中身を見てみる
一応確認します。
1 2 |
sqlite> SELECT * FROM app_fuga WHERE hoge_id = 1 sqlite> |
?結果がありません。どゆこと?
つまり、hoge_idが「1」となっているレコードが無いのにもかかわらず、「あるし!」と言われているので、訳が分かりません。
結論から言うと、おそらく「app/migrations」に、過去のモデル修正の繰り返しで作成されたマイグレーションファイル(000*_auto_YYYYMMDD_TT.pyとか)が蓄積されている状況で、これらが悪さをしているようです。
新しいマイグレーションファイルは、さらに過去のマイグレーションファイルを参照し、変更の履歴を見てコンフリクトを解消しようとします。
つまりその流れの中で、過去のマイグレーションで行ってきた操作ができなかった時、例えば前提とされているテーブル構造が実際には変わっていたり、あるはずのレコードが削除されてたりすると、途中経過でエラーとなる場合があるみたいです。
今回はおそらく、モデルに新しくデフォルト値などを張った都合で整合性が取れなくなったのかと思います。
解決方法
開発環境なので、手順なんていちいち記録していないことも多いでしょうから、どこで狂ったのかなんてわかりません。
そういう時はマイグレーションファイルを削除するのが手っ取り早いのですが、この場合、全てのアプリのマイグレーションファイルを削除してはいけません。全て削除すると、また同じ結果になります。
おそらくmakemigrationsの順番が関係しているのでしょう。
ポイントは、エラーが出ているテーブルのモデルを定義しているマイグレーションファイルをだけを削除します。
今回は「app_fuga」でエラーが出ているので、「app/migrations」の中の、__init__.py以外を全て削除します。
こうすることで、再度makemigrationsしたときに、どうやら変更履歴を無視して、現在のモデル構造だけを反映したマイグレーションファイルを作ってくれるようです。
無事にmigrateが通りました。