DjangoのModel定義のon_delete
について改めて分かったことを簡単にまとめます。
以下のような要点を改めて知る機会がありました。
どういうこと?
まず、on_deleteについてリファレンスに以下の説明があります。
When an object referenced by a ForeignKey is deleted, Django will emulate the behavior of the SQL constraint specified by the on_delete argument. For example, if you have a nullable ForeignKey and you want it to be set null when the referenced object is deleted:
https://docs.djangoproject.com/en/5.0/ref/models/fields/#django.db.models.ForeignKey.on_delete
ここではSET_NULL
の例を記述していますが、on_delete
の内容によってSQL制約の振る舞いをエミュレートする、とあります。
また、以下のような記述もあり、データベースにSQL制約を作成しないとあります。
https://docs.djangoproject.com/en/5.0/ref/models/fields/#django.db.models.ForeignKey.on_delete
あくまでも、データベースではなくアプリケーション側(Django側)であるという説明をしてくれていると理解しています。
こういう事があるよ
データベースに対して制約をかけていない、という点を理解していないとロールバックが必要な際に取るべき手順を誤ってしまう可能性があります。
例えば以下の条件が存在していると仮定します。
task
というアプリケーションForeignKey
にon_delete=PROTECT
を設定していたモデルが存在する
そこから、on_delete=CASCADE
に変更する場合、以下の条件で実施します。
on_delete=CASCADE
にモデルを変更するpython manage.py makemigrations
でマイグレーションファイルを作成するpython manage.py migrate
でマイグレーションを実行する
2.の手順を踏んだ際にはcascade
の変更が記述されたマイグレーションファイルが作成され、以下のようになったと仮定します。
0001_initial.py
0002_change_cascade.py
# このファイルが生成されたイメージ
そしてロールバックを実行する際はカラム追加のようなケースであれば python manage.py task migrate 0001
と実行しますが、
データベースレベルでは何も制約がないため、on_delete
の変更の場合、実行した所で何も意味がない状態になってしまいます。
つまり、今回のケースで言うとPROTECT
に戻ったように見えて実際はCASCADE
のまま、のような状態です。
この場合、ファイルをrevertすることで期待するロールバックを実行する事が可能になります。
実は失敗談
私が勘違いしていた内容でした。 自社の書籍かつ愛読書である自走プログラマーではデータマイグレーションの例ですが、ロールバックができることを確認しようという節がありました。
今回のようなケースでも念の為、ロールバックを出来ることを確認しておこう、と思ってロールバック後に動作確認して意図する挙動にならなかったので「なぜ?」ということになった感じでした。
そして、挙動については公式リファレンスにも載っているので、ちゃんと読まないといけないことを改めて実感・反省しました。
ちなみに、自走プログラマーにはそういう節もあります。 33:公式ドキュメントを読もう
とはいえ、この記事をどこかで見た誰かがそうだったのかーと思えると良いなと思っています。
この記事を書くにあたって調べてみたところ、Modelで作成されたテーブルの内容と、実際にSQLを書いて作成したテーブルの内容では差異もあったのでそうしたところもちゃんと調べてしていけると良いな、と思っています。