LoginSignup
4
0

More than 1 year has passed since last update.

Django の外部キー on_deleteの引数 DO_NOTHING は「何もしない」のか…!?

Last updated at Posted at 2022-12-09

モデルに外部キーを設定するときに、
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_NOTHINGPROTECT の下位互換なので選択する理由が無い

  • 削除させるか判断し辛い段階では、とりあえず PROTECT を選択しておくのが良さそう

  • 削除を許可するには、 SET 系を明示的に選ぶ必要がある

4
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0