覆盖Django中的QuerySet.delete()

Chr*_*att 19 django

我有一个Django模型,它将设置核心保存到应用程序的功能中.你永远不应该删除这个模型.我正在尝试强制执行此应用程序.我在管理员中禁用了删除功能,并且还禁用了模型上的删除方法,但QuerySet有自己的删除方法.例:

MyModel.objects.all()[0].delete() # Overridden, does nothing

MyModel.objects.all().delete() # POOF!
Run Code Online (Sandbox Code Playgroud)

具有讽刺意味的是,Django文档说这有关于为什么delete()是QuerySet上的方法而不是Manager的原因:

这是一种安全机制,可以防止您意外地请求Entry.objects.delete(),并删除所有条目.

如何包括.all()"安全机制"至少可以说是有问题的.相反,这有效地创建了一个无法通过传统方式关闭的后门(覆盖管理器).

任何人都有一个线索如何在没有猴子修补源的情况下在像QuerySet这样的核心上覆盖这个方法?

man*_*nji 45

您可以通过覆盖方法来覆盖Manager's默认值.QuerySetManager.get_query_set()

例:

class MyQuerySet(models.query.QuerySet):

    def delete(self):
        pass  # you can throw an exception


class NoDeleteManager(models.Manager):
    def get_query_set(self):
        return MyQuerySet(self.model, using=self._db)

class MyModel(models.Model)
    field1 = ..
    field2 = ..


    objects = NoDeleteManager()
Run Code Online (Sandbox Code Playgroud)

现在,MyModel.objects.all().delete()什么都不做.

有关更多信息:修改初始Manager QuerySet

  • 来自django 1.6+的方法称为`get_queryset(self)`. (6认同)
  • 除非我弄错了,你可以用[QuerySet.as_manager](https://docs.djangoproject.com/en/1.10/topics/db/managers/#creating-a-manager-with-queryset)做得更清楚一点. -方法).您可以删除上面的管理器类,然后执行:`objects = MyQuerySet.as_manager()` (3认同)

dno*_*zay 6

mixin方法

https://gist.github.com/dnozay/373571d8a276e6b2af1a

使用@manji发布的类似食谱,

class DeactivateQuerySet(models.query.QuerySet):
    '''
    QuerySet whose delete() does not delete items, but instead marks the
    rows as not active, and updates the timestamps
    '''
    def delete(self):
        self.deactivate()

    def deactivate(self):
        deleted = now()
        self.update(active=False, deleted=deleted)

    def active(self):
        return self.filter(active=True)


class DeactivateManager(models.Manager):
    '''
    Manager that returns a DeactivateQuerySet,
    to prevent object deletion.
    '''
    def get_query_set(self):
        return DeactivateQuerySet(self.model, using=self._db)

    def active(self):
        return self.get_query_set().active()
Run Code Online (Sandbox Code Playgroud)

并创建一个mixin:

class DeactivateMixin(models.Model):
    '''
    abstract class for models whose rows should not be deleted but
    items should be 'deactivated' instead.

    note: needs to be the first abstract class for the default objects
    manager to be replaced on the subclass.
    '''
    active = models.BooleanField(default=True, editable=False, db_index=True)
    deleted = models.DateTimeField(default=None, editable=False, null=True)
    objects = DeactivateManager()

    class Meta:
        abstract = True
Run Code Online (Sandbox Code Playgroud)

其他有趣的东西