今回のお題
今回のお題はdjangoアプリでの論理削除の使い方
です。
ユーザーの論理削除(退会処理)はAbstractUser
クラスに最初から実装されていますが他のモデルについては自分で用意する必要があるので、メモとしてまとめておきます。
論理削除とは
レコードを削除した後も情報を参照できるようにすること。
例えば会員制のサービスでは退会後も過去の利用履歴の確認などができるようにしておく必要があるため、物理削除(=通常の削除)ではなく論理削除が使われる。
内部的にはdeleted_at
というフィールドを用意し、この属性値の有無を用いて削除されているかいないかを判定する。
django_boost
論理削除を手軽に実装できるパッケージ。
具体的な使い方は後述。
導入方法は、pipでのインストールとINSTALLED_APPS
への追加のみ。
ターミナル
% pip3 install django_boost
settings.py
INSTALLED_APPS = [
# 中略
"django_boost",# 追加
]
LogicalDeletionMixin
django_boost
を導入することで実装可能になるクラス。
このクラスを継承させたモデルでは論理削除を扱うことができる。
Mixinと名前がついているがmodels.Model
を継承しているため、これ単体でモデルの親とすることが可能(汎用ビューのように多重継承する必要はない)。
LogicalDeletionMixinのソースコード
class LogicalDeletionMixin(models.Model):
"""Provide logical delete."""
deleted_at = models.DateTimeField(
verbose_name=_("deleted date"), blank=True, null=True, default=None, editable=False)
class Meta:
abstract = True
objects = LogicalDeletionManager()
@classmethod
def get_deleted_value(cls):
return now()
def delete(self, using=None, keep_parents=False, hard=False):
if hard:
return super().delete(using=using, keep_parents=keep_parents)
self.deleted_at = self.get_deleted_value()
return self.save()
def revive(self, force_update=False, using=None):
"""Revive logical deleted item."""
self.deleted_at = None
return self.save()
def is_dead(self):
"""Return True if the item is dead."""
return self.deleted_at is not None
def is_alive(self):
"""Return True if record is alive, otherwise False."""
return self.deleted_at is None
論理削除の使い方
# 対象のインスタンスを論理削除
item.delete()
# 既存のdelete()メソッドが論理削除に置き換わります(DeleteViewを通した場合などを含めて)。
# 物理削除
item.delete(hard=True)
# 論理削除された日時の取得
item.deleted_at # 論理削除実行時に自動で設定される
# 論理削除されているかどうかの判定
item.is_alive() # 削除済みならfalse
item.is_dead() # 削除済みならtrue
# テンプレートの{% %}で使う際には最後の()はつけない
# 論理削除の状態に合わせたレコードの前取得
Item.objects.alive() # 削除されていないもののみ
Item.objects.dead() # 削除済みのみ
Item.objects.all() # 削除の有無に関わらず全て
# こちらもテンプレートでは()をつけない