diff --git a/backend/dvadmin/utils/models.py b/backend/dvadmin/utils/models.py index 1283351c61e8a18131f6de61e565098f2e6e21dc..8a6c20f56f8d3af0e73dc9ce4a09c03993c8e1d8 100644 --- a/backend/dvadmin/utils/models.py +++ b/backend/dvadmin/utils/models.py @@ -17,68 +17,127 @@ from rest_framework.request import Request table_prefix = settings.TABLE_PREFIX # 数据库表名前缀 - class SoftDeleteQuerySet(models.QuerySet): - pass + """ + 软删除QuerySet + """ + def delete(self): + """批量软删除""" + return self.update(is_deleted=True, deleted_at=timezone.now()) -class SoftDeleteManager(models.Manager): - """支持软删除""" + def hard_delete(self): + """批量物理删除""" + return super().delete() + + def alive(self): + """只返回未删除的对象""" + return self.filter(is_deleted=False) - def __init__(self, *args, **kwargs): - self.__add_is_del_filter = False - super(SoftDeleteManager, self).__init__(*args, **kwargs) + def dead(self): + """只返回已删除的对象""" + return self.filter(is_deleted=True) - def filter(self, *args, **kwargs): - # 考虑是否主动传入is_deleted - if not kwargs.get('is_deleted') is None: - self.__add_is_del_filter = True - return super(SoftDeleteManager, self).filter(*args, **kwargs) + def all_objects(self): + """返回所有对象(包括已删除)""" + return self + + +class SoftDeleteManager(models.Manager): + """ + 软删除管理器 + """ def get_queryset(self): - if self.__add_is_del_filter: - return SoftDeleteQuerySet(self.model, using=self._db).exclude(is_deleted=False) - return SoftDeleteQuerySet(self.model).exclude(is_deleted=True) + """默认只返回未删除对象""" + return SoftDeleteQuerySet(self.model).filter(is_deleted=False) + + def all_objects(self): + """返回所有对象(包含已删除)""" + return SoftDeleteQuerySet(self.model) + + def deleted(self): + """只返回已删除对象""" + return self.all_objects().filter(is_deleted=True) def get_by_natural_key(self, name): - return SoftDeleteQuerySet(self.model).get(username=name) + """按自然键获取对象(只获取未删除的)""" + return self.get(username=name) class SoftDeleteModel(models.Model): - """ - 软删除模型 - 一旦继承,就将开启软删除 - """ - is_deleted = models.BooleanField(verbose_name="是否软删除", help_text='是否软删除', default=False, db_index=True) + is_deleted = models.BooleanField( + verbose_name="是否软删除", help_text="是否软删除", default=False, db_index=True + ) + deleted_at = models.DateTimeField( + verbose_name="删除时间", null=True, blank=True, db_index=True + ) objects = SoftDeleteManager() class Meta: abstract = True - verbose_name = '软删除模型' - verbose_name_plural = verbose_name - def delete(self, using=None, soft_delete=True, *args, **kwargs): + def delete(self, using=None, soft_delete=True): """ - 重写删除方法,直接开启软删除 + 重写删除方法 + :param soft_delete: 是否软删除 (默认为True) """ if soft_delete: self.is_deleted = True - self.save(using=using) - # 级联软删除关联对象 - for related_object in self._meta.related_objects: - related_model = getattr(self, related_object.get_accessor_name()) - # 处理一对多和多对多的关联对象 - if related_object.one_to_many or related_object.many_to_many: - related_objects = related_model.all() - elif related_object.one_to_one: - related_objects = [related_model] - else: - continue + self.deleted_at = timezone.now() + + update_fields = ["is_deleted", "deleted_at"] + if hasattr(self, "update_datetime"): + self.update_datetime = timezone.now() + update_fields.append("update_datetime") - for obj in related_objects: - obj.delete(soft_delete=True) + self.save(update_fields=update_fields) + + self._cascade_soft_delete() else: - super().delete(using=using, *args, **kwargs) + super().delete(using=using) + + def _cascade_soft_delete(self): + """ + 级联软删除关联对象 + - 只处理设置了 on_delete=models.CASCADE 的关系 + - 跳过多对多关系(只解除关系,不删除对象) + """ + for related in self._meta.related_objects: + if related.on_delete != models.CASCADE: + continue + + accessor_name = related.get_accessor_name() + + if not hasattr(self, accessor_name): + continue + related_manager = getattr(self, accessor_name) + + if related.one_to_many or related.one_to_one: + if hasattr(related_manager, "all"): + related_manager.all().delete() + else: + related_manager.delete() + + elif related.many_to_many: + related_manager.clear() + + def restore(self): + """ + 恢复软删除的对象 + """ + if not self.is_deleted: + return + + self.is_deleted = False + self.deleted_at = None + + update_fields = ["is_deleted", "deleted_at"] + if hasattr(self, "update_datetime"): + self.update_datetime = timezone.now() + update_fields.append("update_datetime") + + self.save(update_fields=update_fields) class CoreModelManager(models.Manager):