Django、on_deleteを使う(2.0から必須)

2018-11-09 / PythonDjango

on_deleteとは

以下のような、シンプルなモデルがあります。

from django.db import models


class Category(models.Model):
    """カテゴリ"""
    name = models.CharField(max_length=255)

    def __str__(self):
        return self.name


class Post(models.Model):
    """記事"""
    title = models.CharField(max_length=255)
    text = models.TextField()
    category = models.ForeignKey(Category)

    def __str__(self):
        return self.title

試しに記事をいくつか作成し、全て食べ物というカテゴリにしたとします。
全て食べ物カテゴリ

6個記事をつくり、すべて食べ物カテゴリにしました。
全て食べ物カテゴリ

この状態で食べ物というカテゴリを削除すると...
カテゴリを削除

食べ物を指定した記事も、一緒に削除されます。
一緒に削除

以前、このブログでカテゴリを整理しようと思った際に同様のことをやってしまい グーグル検索のキャッシュを見ながら人力で記事を作成しなおすという不毛な作業を行ったことは記憶に新しいです。

このような動作は、on_deleteでふるまいを変更できます。

category = models.ForeignKey(Category, on_delete=models.CASCADE)

models.CASCADE - 一緒に削除される

category = models.ForeignKey(Category, on_delete=models.CASCADE)

管理画面で試すと、以下のように紐づいている情報も表示され、削除するとそれらも一緒に削除されます。便利な反面、怖い部分もある指定です。

models.PROTECT - 保護する。参照されてたら、削除ができない

category = models.ForeignKey(Category, on_delete=models.PROTECT)

削除を試みると...
削除を試みる

紐づいているデータがあるので、削除できないと表示されますね。
削除できないと表示

ただし、そのカテゴリがどの記事からも指定されてなければ削除はできます。
どの記事からも指定されてなければ削除はできます

何を指定するか迷ったら、このmodels.PRETECTがお勧めです。知らない間にデータを消してしまった、という事態も防げます。

models.SET_NULL - NULLをセットする

これを使うには、フィールドの引数にnull=Trueでnullを許容するようにするひつようがあります。nullを許可していいのなら、これも有用です。

category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)

削除してみると...
削除してみる

ちゃんと空欄(null)になりますね。
空欄(null)

models.SET_DEFAULT - デフォルト値をセットする

SET_NULLのように、これも'default='という引数を指定しておくひつようがあります。

def set_default_category():
    category, _ = Category.objects.get_or_create(name='飲み物')
    return category


class Post(models.Model):
    """記事"""
    title = models.CharField(max_length=255)
    text = models.TextField()
    category = models.ForeignKey(
        Category,
        on_delete=models.SET_DEFAULT,
        default=set_default_category
    )

on_deleteというよりはdefault引数についてのお話になりますが、ForeignKeyのdefault引数には

といったものを与えることができます。関数を渡す方法が一番無難です。上の例では結果的にカテゴリのデフォルト値として飲み物が設定されます。Category.objects.get_or_create(name='飲み物')は、飲み物カテゴリがあればそれを返し、ない場合はその場で飲み物カテゴリを作るという処理をします。

食べ物カテゴリを設定しています(defaultは飲み物)。
食べ物カテゴリ

削除してみると...
削除してみる

ちゃんとdefault=で指定した飲み物カテゴリに変わりますね。
飲み物カテゴリ

models.SET() - 値を設定する

削除されたときに、他の値を設定することができます。

値を直接渡すこともできますし、関数(正確には呼び出し可能オブジェクト)を渡すこともできます。関数を渡す方が確実でしょう。

def make_and_set_category():
    category, _ = Category.objects.get_or_create(name="New")
    return category


class Post(models.Model):
    """記事"""
    title = models.CharField(max_length=255)
    text = models.TextField()
    category = models.ForeignKey(
        Category,
        on_delete=models.SET(make_and_set_category),
    )

    def __str__(self):
        return self.title

上の例では、紐づいているカテゴリが削除されるとmake_and_set_category関数が呼び出されま、結果的にNewという名前のカテゴリが設定されます。

試しに削除してみると...
試しに削除

Newカテゴリというものがいつのまにか作られ...
Newカテゴリ

記事にNewカテゴリがセットされました。
記事にNewカテゴリがセット

この記事の関連記事

関連記事はありません。

コメント欄

記事にコメントする

まだコメントはありません。