问题:这将导致级联断裂。我原本希望发生的级联是通过我在模型和自定义查询集上覆盖的方法发生的。实际发生的是,它们被默认查询集/管理器绕过,后者还偶尔使用快速的_raw_delete内部API。因此,要么不发生级联删除,要么如果我在模型上调用super().delete()方法(并在此之后保存(save())),则会对相关对象执行标准删除。
我已经尝试了Cascading Delete w/ Custom Model Delete Method中建议的方法,但这会使事情变得非常糟糕 - 除此之外,它倡导使用已弃用的use_for_related_fields管理器属性。
我开始觉得,如果没有影响Django的私有部分,则无法实现我想要实现的内容。这很奇怪,因为软删除行为在许多DBMS情况下都是一种标准模式。
这就是我目前的状态:
I created a custom manager and query set for objects with a is_deleted field:
from django.db import models from django.db.models.query import QuerySet class SoftDeleteQuerySet(QuerySet): #https://dev59.com/Kl4b5IYBdhLWcg3wnS0e 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)
Added a base model to use it:
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
And admin classes to handle the erasure, restore, etc:
# 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
有什么想法?
编辑:所有这些的要点(除了更彻底地搜索打包解决方案 :-))是覆盖删除并正确执行可以完成,但它并不容易,也不适合弱者。我将使用的软件包 - 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()。
pre_delete
或post_delete
信号。 - Chiefir