on_delete属性
Djangoには外部キーを表すForeignKeyと言うものがModelに存在する
デフォルトはmodels.CASCADE
関連モデルが削除されたときに、どう言う挙動にするのか設定するオプション群
例えば
著者テーブル
本テーブル
が存在して、著者テーブルを削除したときに関連する本も削除するかどうか・・・という感じ
あるいは、
受注ヘッダ
受注明細
が存在して、受注ヘッダを削除した時にForeignKeyに設定している受注明細を削除するかどうか、という感じ
CASCADE
そもそも、SQLには前提としてCASCADEという制約が存在しているみたい
CASCADE: Delete or update the row from the parent table and automatically delete or update the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported. Between two tables, do not define several ON UPDATE CASCADE clauses that act on the same column in the parent table or in the child table.
Automatically truncate all tables that have foreign-key references to any of the named tables, or to any tables added to the group due to CASCADE.
CASCADE: A "CASCADE" action propagates the delete or update operation on the parent key to each dependent child key. For an "ON DELETE CASCADE" action, this means that each row in the child table that was associated with the deleted parent row is also deleted. For an "ON UPDATE CASCADE" action, it means that the values stored in each dependent child key are modified to match the new parent key values.
そういう便利なものがあるのかーと思っていたけど、言われてみたら?ForeignKeyとしてリレーションを持っているなら、削除されないことも違和感ないかと思った
PROTECT
削除しない。
削除しようとした時にProtectedErrorを発生させる
RESTRICT
削除しようとした時にRestrictErrorを発生させる
PROTECTとは異なるらしい
一言で言うと、他のCASCADEで削除される時は一緒に削除されるみたい?
単体での削除?(表現が適当かわからない)は防ぐが、
CASCADEでの削除は防ぐことができる
こんな認識
やっぱりちょっと複雑?な感じっぽい
以下のモデルを自分でも作成した方が良さそう
公式リファレンスのコードより
>>> artist_one = Artist.objects.create(name="artist one")
>>> artist_two = Artist.objects.create(name="artist two")
>>> album_one = Album.objects.create(artist=artist_one)
>>> album_two = Album.objects.create(artist=artist_two)
>>> song_one = Song.objects.create(artist=artist_one, album=album_one)
>>> song_two = Song.objects.create(artist=artist_one, album=album_two)
>>> album_one.delete()
# Raises RestrictedError.
>>> artist_two.delete()
# Raises RestrictedError.
>>> artist_one.delete()
(4, {'Song': 2, 'Album': 1, 'Artist': 1})
album_oneは削除しようとして、RestrictedErrorが発生
artist_twoも削除しようとして、RestrictedErrorが発生
artist_oneは削除しようとしたら、削除が出来た
Song, Album, Artistの関連データが削除できていることがわかる
ちょっとコードを入れ替えてみる
これは成功する、関連したものが削除されていそうだとわかる
>>> artist_one = Artist.objects.create(name="artist one") ?
>>> album_one = Album.objects.create(artist=artist_one)
>>> album_two = Album.objects.create(artist=artist_two)
>>> song_one = Song.objects.create(artist=artist_one, album=album_one)
>>> song_two = Song.objects.create(artist=artist_one, album=album_two)
>>> artist_one.delete()
(4, {'Song': 2, 'Album': 1, 'Artist': 1})
失敗するケース
>>> artist_one = Artist.objects.create(name="artist one")
>>> artist_two = Artist.objects.create(name="artist two")
>>> album_two = Album.objects.create(artist=artist_two)
>>> song_two = Song.objects.create(artist=artist_one, album=album_two)
>>> artist_two.delete()
# Raises RestrictedError.
songのRESTRICTが聞いているからか? 削除はできない
一番親が未対応だから、削除できていないということ?
>>> artist_one = Artist.objects.create(name="artist one")
>>> album_one = Album.objects.create(artist=artist_one)
>>> song_one = Song.objects.create(artist=artist_one, album=album_one)
>>> album_one.delete()
# Raises RestrictedError.
つまり、artist_oneを削除しようとした時に、関連データは削除することができる(エラーは発生しない)
code:python
>> artist_one = Artist.objects.create(name="artist one")
INSERT INTO "musics_artist" ("name")
VALUES ('artist one') RETURNING "musics_artist"."id"
>> artist_two = Artist.objects.create(name="artist two")
INSERT INTO "musics_artist" ("name")
VALUES ('artist two') RETURNING "musics_artist"."id"
>> album_one = Album.objects.create(artist=artist_one)
INSERT INTO "musics_album" ("artist_id")
VALUES (1) RETURNING "musics_album"."id"
>> album_two = Album.objects.create(artist=artist_two)
INSERT INTO "musics_album" ("artist_id")
VALUES (2) RETURNING "musics_album"."id"
>> song_one = Song.objects.create(artist=artist_one, album=album_one)
INSERT INTO "musics_song" ("artist_id", "album_id")
VALUES (1, 1) RETURNING "musics_song"."id"
>> song_two = Song.objects.create(artist=artist_one, album=album_two)
INSERT INTO "musics_song" ("artist_id", "album_id")
VALUES (1, 2) RETURNING "musics_song"."id"
テーブルで言うと以下のような状態
table: musics_artist
id name delete()したらどうなるか
1 artist one (4, {'Song': 2, 'Album': 1, 'Artist': 1})
2 artist two raise RestrictedError => Song: artist one Album object (2) があるから駄目
artist two を削除しようとする
Songも一緒に削除しようとなるのだが・・・
songのartistにはartist oneが紐づいている = artist oneはartist twoと関連していないので、削除できない、と理解
table: musics_album
id artist_id delete()したらどうなるか
1 1 raise RestrictedError
2 2 raise RestrictedError
table: musics_song
id album_id artist_id delete()したらどうなるか
1 1 1 songとartistとalbumが削除される
2 2 1 songとartistとalbumが削除される
分かりやすくすると
table:songs可視化
album artist 削除
artist one artist one できる
artist two artist one できない
table: musics_artist
id name album_id
1 artist one
2 artist two 2
つまり、
SET_NULL
SET_DEFAULT
SET()
Set the ForeignKey to the value passed to SET(), or if a callable is passed in, the result of calling it. In most cases, passing a callable will be necessary to avoid executing queries at the time your models.py is imported:
DO_NOTHING
ForeignKey.limit_choices_to
ForeignKey.related_name
ForeignKey.related_query_name
ForeignKey.to_field
ForeignKey.db_constraint
ForeignKey.swappable