Django、on_deleteを使う(django2.0から必須)

Python - Django
2018年11月10日2:19に更新(約4日前)
2018年11月9日17:00に作成(約4日前)

旧ブログ移行記事す。

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)になりますね。

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

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

    category = models.ForeignKey(
        Category,
        on_delete=models.SET_DEFAULT,
        default=Category.objects.get(name="飲み物"),
    )

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

削除してみると...

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

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

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

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

def make_and_set():
    category = Category.objects.get_or_create(name="New")[0]
    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),
    )

    def __str__(self):
        return self.title

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

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

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

以下はなければ作り、あればそのまま返す、という処理です。

Category.objects.get_or_create(name="New")[0]

記事にコメントする