DjangoBrothers BLOG ✍️

2018/03/25

このエントリーをはてなブックマークに追加
Django モデル クエリセット ForeignKey

Django2.0から必須になったon_deleteの使い方

モデル同士を紐づけるときに、ForeignKeyOneToOneFieldを使いますが、その際、バージョン2からは引数としてon_deleteを指定することが必須となりました。

指定しないとTypeError: init() missing 1 required positional argument: 'on_delete'というエラーメッセージが出てしまいます。

on_deleteの意味

on_deleteとは、参照するオブジェクトが削除されたときに、それと紐づけられたオブジェクトも一緒に削除するのか、それともそのオブジェクトは残しておくのかを設定するものです。

実際の使用例

例えば、こんなモデル設計があったとします。ForeinKeyを使用して、Storyオブジェクト(小説)はAuthorオブジェクト(作者)を参照しています。

models.py

from django.db import models

class Author(models.Model):
    """作者"""
    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name

class Story(models.Model):
    """小説"""
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    title = models.CharField(max_length=50)
    text = models.TextField()

    def __str__(self):
        return self.title

この設計によってこんなStorysテーブルができます。

例えばここでAuthorオブジェクト:"川端康成"を削除したときに、それと紐づいたStoryオブジェクト:"雪国"、"伊豆の踊り子"も削除するのか、それとも残しておくのかを設定するのがon_deleteです。

on_deleteの設定は以下6種類の中から選択します。

  1. CASCADE
  2. PROTECT
  3. SET_NULL
  4. SET_DEFAULT
  5. SET()
  6. DO_NOTHING

それぞれどのような設定になるのか見ていきましょう。

1. CASCADE

models.py

author = models.ForeignKey(Author, on_delete=models.CASCADE)

削除するオブジェクトに紐づいたオブジェクトを全て削除します。

例えば、以下の画像のようにAuthorオブジェクト"川端康成"を削除しようとして削除ボタンを押すと、

このように「関連付けられてるオブジェクトを削除します」とメッセージが出ます。

「はい」ボタンで、Authorオブジェクト"川端康成"が削除されるのと同時に、それに紐づいたStoryオブジェクトも全て削除されます。

2. PROTECT

models.py

author = models.ForeignKey(Author, on_delete=models.PROTECT)

関連付けられてるオブジェクトがあると、削除できません。

この場合、Authorに紐づいたStoryがあるのでAuthorを削除することができません。関連付けられたStoryオブジェクトを全て削除すればAuthorも削除することができるようになります。

3. SET_NULL

models.py

author = models.ForeignKey(
    Author,
    on_delete=models.SET_NULL,
    null=True
)

オブジェクトが削除されると、代わりにNULLをセットします。

今回の例だと、Authorを削除するとStoryオブジェクトのauthorフィールドがNULL(空白)になります。

こんな感じで、Authorに誰も登録されていない状態になります。注意点として、authorフィールドはnull=Trueにしておく必要があります。

4. SET_DEFAULT

models.py

author = models.ForeignKey(
    Author,
    on_delete=models.SET_DEFAULT,
    default=Author.objects.get(name="匿名")
)

削除されたオブジェクトの代わりに、デフォルト値が入るようになります。注意点として、authorフィールドにはデフォルト値を設定しておく必要があります。

このようにauthorフィールドにデフォルト値の"匿名"が入ります。

5. SET()

自分で処理を自由に設定することができます。

今回は、"削除された著者"という文字列をauthorフィールドに入れる処理をします。

models.py

def get_deleted_user():
    return Author.objects.get_or_create(name="削除された作者")[0]

class Story(models.Model):
    author = models.ForeignKey(Author, on_delete=models.SET(get_deleted_user))
    title = models.CharField(max_length=50)
    text = models.TextField()

6. DO_NOTHING

何の処理もしません。

まとめ

基本的にはCASCADEPROTECTを使う場面が多いのではないかと思います。いずれにせよ、on_deleteを指定しないとエラーが出てしまうので気をつけましょう。