モデルに外部キーを設定するときに、
on_delete=models.DO_NOTHING
とした時の挙動について、発見があったので共有します。
外部キー on_delete のおさらい
モデルA がモデルBの外部キー持つ場合に、モデルBを削除しようとする時に、Aの振る舞いを決める
指定する挙動は、大きく次の3タイプに分かれる
①PROTECT
Aが存在する時、Bは削除できない
②CASCADE
Aが存在する時、Bを削除可能で、Aも同時に消える
③SET_NULL / SET_DEFAULT
Aが存在する時、Bを削除可能で、Bの外部キーの値に代わりにNull やデフォルト値を入れる
DO_NOTHING
は、上記のように明示的に指定したくない場合に使えると思っていた。
強いて言えば、Aが存在していても、Bを削除出来る ③ に近い挙動を取ると思っていた。
ところが、実際の挙動は ①とほぼ同じ で、Aが存在する時、Bを削除できなかった。
どういうことかとDBを調べてみると、次のような制約が付いている。
model_a_field_9cdf3fca_fk_model_b_id
これは、PROTECT
を選んだ時に付与されるものと全く同じ。
外部キーを作る時点でこの制約がつくようで、これにより、DB的挙動は PROTECT
と全く同じとなる。
検証
では、PROTECT
と何が違っているのか。
というか、何が DO_NOTHING
なのか。
(制約つけてるやん、削除できないやん)
実際に DO_NOTHING
を設定して管理画面からAが存在する状態でBモデルを削除しようとすると、500エラーが表示された。
やはりBを削除はできない。
PROTECT
に変えてから削除してみるとすると、500エラーにはならず、
Aのモデルが存在する為、削除できません
と削除出来ない理由が表示された。
なるほど!
PROTECT
は削除できない理由を教えてくれる
DO_NOTHING
は挙動は同じでも、500エラーをそのまま返す(=何もしない)
つまり、DO_NOTHING
を選んでも、Aが存在する場合はBを削除できない。
結論
-
DO_NOTHING
はPROTECT
の下位互換なので選択する理由が無い -
削除させるか判断し辛い段階では、とりあえず
PROTECT
を選択しておくのが良さそう -
削除を許可するには、
SET
系を明示的に選ぶ必要がある