【问题标题】:Overriding QuerySet.delete() in Django在 Django 中覆盖 QuerySet.delete()
【发布时间】:2011-09-21 12:34:09
【问题描述】:

我有一个 Django 模型,其中包含应用程序功能的设置核心。你永远不应该删除这个模型。我正在尝试在整个应用程序范围内执行此操作。我已经在后台禁用了删除功能,也禁用了模型上的删除方法,但是 QuerySet 有它自己的删除方法。示例:

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

MyModel.objects.all().delete() # POOF!

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

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

至少可以说,必须包含.all() 是一种“安全机制”。相反,这实际上创建了一个无法通过常规方式(覆盖管理器)关闭的后门。

任何人都知道如何在像 QuerySet 这样核心的东西上覆盖这个方法而不用猴子修补源?

【问题讨论】:

    标签: django


    【解决方案1】:

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

    示例:

    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()
    

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

    欲了解更多信息:Modifying initial Manager QuerySets

    【讨论】:

    • 嗯,这对我来说是个大问题。我自己应该想到这一点,但感谢您补充了我老化的大脑的缺陷;)。
    • 来自 django 1.6+ 的方法称为 get_queryset(self)
    • 除非我弄错了,否则您可以使用QuerySet.as_manager 更简洁地执行此操作。你可以删除上面的经理类,然后做:objects = MyQuerySet.as_manager()
    • 到目前为止(Django 1.11)“get_queryset”方法看起来像def get_queryset(self): return MyQuerySet(model=self.model, using=self._db, hints=self._hints)
    【解决方案2】:

    混合方法

    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()
    

    并创建一个 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
    

    其他有趣的东西

    【讨论】:

      猜你喜欢
      • 2011-09-24
      • 2019-01-01
      • 2020-10-07
      • 2014-08-15
      • 1970-01-01
      • 2017-08-23
      • 2020-11-08
      • 2011-11-13
      • 2012-04-19
      相关资源
      最近更新 更多