論理削除が良い悪いの話はおいておいて、金融系のシステム等では過去のいかなるデータも物理歴に(一定期間)削除してはならず、論理削除を使うことがある。DRFでの論理削除の実装方法についていくつかの記事を見てきたが、僕らはこんな形で実装したので、思い切って公開する。
論理削除対応のモデル
class XQuerySet(models.QuerySet):
def get_or_none(self, *args, **kwargs):
try:
return super(XQuerySet, self).get(*args, **kwargs)
except ObjectDoesNotExist:
return None
def delete(self):
"""
論理削除
Model.objects.all().delete()
:return:
削除した件数
"""
return super(XQuerySet, self).update(is_active=False, deleted=now_tz())
def delete_hard(self):
"""
物理削除
Model.objects.all().delete_hard()
:return:
削除した件数
"""
return super(XQuerySet, self).delete()
class XModelManager(models.Manager):
def get_or_none(self, **kwargs):
try:
return self.get(**kwargs)
except ObjectDoesNotExist:
return None
def get_queryset(self):
return XQuerySet(self.model)
class XModelManagerActive(XModelManager):
def get_queryset(self):
return XQuerySet(self.model).filter(is_active=True)
class XModel(TimeStampedModel):
is_active = models.BooleanField(
default=True,
editable=False,
help_text="論理削除用のフラグ"
)
deleted = models.DateTimeField(
null=True,
default=None,
editable=False,
help_text="論理削除された時刻"
)
"""
通常検索で利用する Manager
論理削除されたものが表示されない
"""
objects = XModelManagerActive()
"""
全体検索で利用する Manager
論理削除されたものも含めて表示する
"""
entire = XModelManager()
def delete(self, using=None, keep_parents=False, is_hard=False):
if is_hard:
return super().delete(using, keep_parents)
else:
self.deleted = now_tz()
self.is_active = False
self.save(using)
return 1, {}
def delete_hard(self, using=None, keep_parents=False):
self.delete(using, keep_parents, is_hard=True)
class Meta:
abstract = True
ModelManagerを2つ作って使い分けていますが、基本(普段使い)は objects
なのでプログラマが特に意識せずとも論理削除になる仕組みです。
実際のモデル
class Category(XModel):
name = models.CharField(
max_length=32,
null=False,
blank=False,
default="",
help_text="カテゴリ名"
)
key = models.CharField(
max_length=32,
null=False,
blank=False,
default="",
help_text="カテゴリ名英語"
)
XModelを継承するだけです。ついでに get_or_none
とかもついてきます。
たとえばAdminなどで論理削除されたデータにもアクセスしたい場合はこうすれば良い
@admin.register(Category)
class AdminUserAdmin(admin.ModelAdmin):
list_display = ('key', 'name')
def get_queryset(self, request):
return self.model.entire.all()
XTechではAPIの開発に Django REST framework
を使っています。DRFのかゆい所に手が届く拡張をたくさん持ってますので、出来る限り公開していきたいと思いますので、乞うご期待で。