Ali*_*orm 7 django django-models
我正在尝试为Django模型实现通用的软删除模式。
为模型提供一个is_deleted字段,该字段将已删除的对象保留在DB中,但出于所有实际目的将其隐藏:应遵循级联等所有常规规则,但实际删除除外。但是,管理应用程序仍应能够使用已删除的对象,以擦除(确定将其丢弃)或还原它们。(请参见下面的代码)
问题:这会中断级联。我原本希望发生的事情是通过我在模型和自定义查询集上覆盖的方法来进行级联。实际发生的是,它们被默认的查询集/管理器绕过,默认查询集/管理器也恰好使用了快速的_raw_delete内部API。因此,要么不会发生级联删除,要么如果我在模型上调用super()。delete()方法(此后再调用save()),则对相关对象执行标准删除。
我已经尝试了“使用自定义模型删除方法进行级联删除”中的建议,但是这很糟糕—除了提倡使用已弃用的use_for_related_fields管理器属性。
我开始认为,要实现我的目标就不可能不影响Django私有对象的主要区别-很奇怪,因为这种软删除行为是许多DBMS情况下的标准模式。
这是我现在的位置:
我为具有is_deleted字段的对象创建了自定义管理器和查询集:
from django.db import models
from django.db.models.query import QuerySet
class SoftDeleteQuerySet(QuerySet):
#/sf/ask/2022736621/
def __init__(self,*args,**kwargs):
return super(self.__class__,self).__init__(*args,**kwargs)
def delete(self,*args,**kwargs):
for obj in self: obj.delete()
#http://codespatter.com/2009/07/01/django-model-manager-soft-delete-how-to-customize-admin/
# but use get_queryset, not get_query_set !!!
class SoftDeleteManager(models.Manager):
""" Use this manager to get objects that have a is_deleted field """
def get_queryset(self,*args,**kwargs):
return SoftDeleteQuerySet(model=self.model, using=self._db, hints=self._hints).filter(is_deleted=False)
def all_with_deleted(self,*args,**kwargs):
return SoftDeleteQuerySet(model=self.model, using=self._db, hints=self._hints).filter()
def deleted_set(self,*args,**kwargs):
return SoftDeleteQuerySet(model=self.model, using=self._db, hints=self._hints).filter(is_deleted=True)
def get(self, *args, **kwargs):
""" if a specific record was requested, return it even if it's deleted """
return self.all_with_deleted().get(*args, **kwargs)
def filter(self, *args, **kwargs):
""" if pk was specified as a kwarg, return even if it's deleted """
if 'pk' in kwargs:
return self.all_with_deleted().filter(*args, **kwargs)
return self.get_queryset().filter(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)添加了使用它的基本模型:
class SoftDeleteModel(models.Model):
objects=SoftDeleteManager()
is_deleted = models.BooleanField(default=False, verbose_name="Is Deleted")
def delete(self,*args,**kwargs):
if self.is_deleted : return
self.is_deleted=True
self.save()
def erase(self,*args,**kwargs):
"""
Actually delete from database.
"""
super(SoftDeleteModel,self).delete(*args,**kwargs)
def restore(self,*args,**kwargs):
if not self.deleted: return
self.is_deleted=False
self.save()
def __unicode__(self): return "%r %s of %s"%(self.__class__,str(self.id))
class Meta:
abstract = True
Run Code Online (Sandbox Code Playgroud)和管理类来处理擦除,还原等操作:
# for definitive deletion of models in admin
def erase_model(modeladmin,request,queryset):
"""
Completely remove models from db
"""
for obj in queryset:
obj.erase(user=request.user)
def restore_model(modeladmin,request,queryset):
"""
Restore a softdeletd model set
"""
for obj in queryset:
obj.restore(user=request.user)
#http://codespatter.com/2009/07/01/django-model-manager-soft-delete-how-to-customize-admin/
# but the method is now get_queryset.
class SoftDeleteAdmin(admin.ModelAdmin):
list_display = ('pk', '__unicode__', 'is_deleted',)
list_filter = ('is_deleted',)
actions=[erase_model, restore_model]
def get_queryset(self, request):
""" Returns a QuerySet of all model instances that can be edited by the
admin site. This is used by changelist_view. """
# Default: qs = self.model._default_manager.get_query_set()
qs = self.model._default_manager.all_with_deleted()
#TR()
# TODO: this should be handled by some parameter to the ChangeList.
ordering = self.ordering or () # otherwise we might try to *None, which is bad ;)
if ordering:
qs = qs.order_by(*ordering)
return qs
queryset=get_queryset
Run Code Online (Sandbox Code Playgroud)有想法吗?
编辑:所有这一切的收获(除了不间断地搜索打包的解决方案:-))是可以执行覆盖删除并正确处理它,但是对于隐隐感到困难的人来说并不容易。我将要使用的软件包-django-softdelete,这是我的起点的演变,从http://codespatter.com/2009/07/01/django-model-manager-soft-delete-how-to-customize翻录-admin / -使用通过Contenttype API计算的ChangeSet。
除此之外,在某些情况下根本不会调用重写的delete()(基本上,每次发生组删除时,django都会采用跳过model.delete()头的快捷方式)。
以我的拙见,这是一个设计失误。如果覆盖它需要消耗如此大量的大脑,那么model.delete()实际上应该是model._delete()。
也许你可以使用django-paranoid。
它类似于 Rails 的acts_as_paranoid,并且易于使用。
您只需要使用 ParanoidModel 扩展您的模型。
要检索已删除的对象,您可以使用objects_with_deleted管理器:
MyModel.objects_with_deleted.last()
Run Code Online (Sandbox Code Playgroud)
如果要对对象执行硬删除,请使用以下True参数:
m = MyModel.objects.last()
m.delete(True)
Run Code Online (Sandbox Code Playgroud)
django-softdelete是一个为 Django 实现软删除的库,具有级联功能。它还存储变更集,并允许恢复删除(例如恢复整个级联)。
我不确定它的维护状态和质量如何,但它至少可以作为灵感,即使它本身不是解决方案。
| 归档时间: |
|
| 查看次数: |
3804 次 |
| 最近记录: |