Django ORM - 用标志软删除对象,但仍能在相关查询中查找它们

时间:2017-02-08 21:29:32

标签: django django-models

我们通过以下方式开发了在Django中进行软删除的快速方法:

  1. 将delete_on字段添加到可能被“软删除”的模型
  2. 在模型中设置objects = OurObjectManger
  3. OurObjectManager只是覆盖了get_queryset并将filter(deleted_on = None)附加到查询集。
  4. 调用instance.soft_delete()设置deleted_on字段
  5. 在实践中运作良好,当传输被删除并且查询不返回时,隐藏传输。

    唯一的问题是我们希望在另一个模型中被外键引用时仍然会显示这些内容。例如,事务模型引用了诸如transaction.transfer之类的Transfer,它现在是Django的None。

    有什么想法吗?

3 个答案:

答案 0 :(得分:0)

只要经理没有:

use_for_related_fields = True

作为类属性,外键查找

transaction.transfer

如果模型实例存在,

仍会填充模型实例。

此处要注意的另一件事是,您应该知道删除引用或被其他模型引用的模型对象需要进行精细处理,以便在调用delete时不会意外丢失任何数据。

e.g。

删除通常的工作方式如下:

Transfer.objects.filter(id=my_id).delete() --> Deletes all other objects related to it by default, sets to null if you like

但是如果你添加了软删除,那就是应用程序级逻辑,如果你这样做的话就是这样:

Transaction.objects.filter(id=another_id).delete()

您可能会无意中删除您不想删除的对象!

如果非软删除模型知道删除不合适,您应该没问题(设置空行为,覆盖模型类和模型管理器上的删除方法)。

供参考,请参阅使用pinax-models

完成的操作

Docs for related managers

答案 1 :(得分:0)

我们最终解决了这个问题,只在我们只需要软对象时调用objects_soft ......这种方式相关的查找已经使用默认对象管理器并找到相关的fk。对于我们的多态模型,这最终成为:

objects = PolymorphicManager()
objects_soft = SoftPolymorphicObjectManager()

使用

查询它们
TransferBase.objects_soft.filter()...

模型扩展了软删除功能:

class SoftDeleteFunctions(object):
    def soft_delete(self):
        self.deleted_on = now()
        self.save()

class SoftPolymorphicObjectManager(PolymorphicManager):
    def get_queryset(self):
        queryset = self.queryset_class(self.model, using=self._db)
        return queryset.filter(deleted_on=None)

class SoftObjectManager(Manager):
    def get_queryset(self):
        queryset = super(Manager, self).get_queryset()
        return queryset.filter(deleted_on=None)

答案 2 :(得分:0)

除了@ kevin-parker的answer外,我还想根据文章Soft Deletion in Django中的想法保留这段代码。我进行了更改以支持多态模型,但是未测试此代码。改变了主意,使用django-polymorphic。

from django.db import models
from django.utils import timezone
from polymorphic.models import PolymorphicModel
from polymorphic.managers import PolymorphicManager
from polymorphic.query import PolymorphicQuerySet

class SoftPolymorphicQuerySet(PolymorphicQuerySet):
    def delete(self):
        return super().update(deleted_at=timezone.now())

    def hard_delete(self):
        return super().delete()

    def alive(self):
        return self.filter(deleted_at=None)

    def dead(self):
        return self.exclude(deleted_at=None)


class SoftPolymorphicDeletionManager(PolymorphicManager):
    queryset_class = SoftPolymorphicQuerySet

    def __init__(self, *args, **kwargs):
        self.alive_only = kwargs.pop('alive_only', True)
        super().__init__(*args, **kwargs)

    def get_queryset(self):
        qs = super().get_queryset()
        if self.alive_only:
            return qs.filter(deleted_at=None)
        return qs

    def hard_delete(self):
        return self.get_queryset().hard_delete()


class SoftPolymorphicDeletionModel(PolymorphicModel):
    deleted_at = models.DateTimeField(blank=True, null=True)

    class Meta(PolymorphicModel.Meta):
        abstract = True

    objects = SoftPolymorphicDeletionManager()
    all_objects = SoftPolymorphicDeletionManager(alive_only=False)

    # For backward compatibility keep original arguments
    def delete(self, *args, **kwargs):
        self.deleted_at = timezone.now()
        self.save()

    # Pass arguments to original delete method
    def hard_delete(self, *args, **kwargs):
        super().delete(*args, **kwargs)