3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Django, DRF(Django REST Framework)での論理削除

Posted at

論理削除が良い悪いの話はおいておいて、金融系のシステム等では過去のいかなるデータも物理歴に(一定期間)削除してはならず、論理削除を使うことがある。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のかゆい所に手が届く拡張をたくさん持ってますので、出来る限り公開していきたいと思いますので、乞うご期待で。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?