发布于 2026-01-06 3 阅读
0

Django AWS AI 中的软删除!

Django 中的软删除

AWS AI 直播!

什么是软删除?

有时,您可能想从数据库中删除某些内容,但实际上并不想删除它们。您希望保留这些数据,以便将来可能需要恢复或用于其他用途,例如数据分析……但这些数据应该看起来像是已被删除,而不是出现在应用程序中意想不到的地方。这被称为“软删除”。

与其从数据库中真正删除记录(这是不可逆的),不如将其“标记”为已删除,通常是在表的另一列中进行标记。现在的挑战是如何防止软删除的记录“泄露”到应用程序中。

在本文中,我们将以一个简单的笔记应用程序后端为例,学习如何在 Django 中实现软删除。

软删除模式

我们将首先创建一个SoftDeleteModel可供其他模型继承的基础模型。

class SoftDeleteModel(models.Model):

    is_deleted = models.BooleanField(default=False)

    def soft_delete(self):
        self.is_deleted = True
        self.save()

    def restore(self):
        self.is_deleted = False
        self.save()

    class Meta:
        abstract = True
Enter fullscreen mode Exit fullscreen mode

请注意,我们已将此模型标记为“未定义” abstract = True。这意味着 Django 不会为其创建数据库表。

现在,我们可以将模型创建为子类,SoftDeleteModel从而赋予它们软删除的能力。让我们以一个模型为例Note

class Note(SoftDeleteModel):

    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="notes")
    title = models.CharField(max_length=32)
    content = models.CharField(max_length=255)
Enter fullscreen mode Exit fullscreen mode

我们可以通过筛选查询数据库,is_deleted以排除已被软删除的记录。

Note.objects.filter(is_deleted=False)
Enter fullscreen mode Exit fullscreen mode

试一试

让我们来尝试运行一下目前为止编写的代码。首先,python manage.py shell在终端中输入命令打开 Django shell。

导入所需模型:

from django.contrib.auth.models import User
from tutorialapp.models import Note
Enter fullscreen mode Exit fullscreen mode

由于每条笔记都与某个用户存在外键关联,因此我们的第一步是创建一个User对象

john = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')
Enter fullscreen mode Exit fullscreen mode

现在我们可以创建几个笔记:

my_note = Note.objects.create(user=john, title="Strawberry Fields", content="Strawberry Fields Forever")
another_note = Note.objects.create(user=john, title="Here Comes The Sun", content="It's All Right")
Enter fullscreen mode Exit fullscreen mode

您现在可以对笔记进行软删除和恢复操作了:

my_note.soft_delete()
my_note.restore()
Enter fullscreen mode Exit fullscreen mode

您可以查询所有笔记,无论它们是否已被软删除:

Note.objects.all()
Enter fullscreen mode Exit fullscreen mode

您还可以筛选出未被软删除的笔记:

Note.objects.filter(is_deleted=False)
Enter fullscreen mode Exit fullscreen mode

软删除管理器

is_deleted=False虽然我们的代码在功能上是正确的,但缺点是每次编写查询时都必须记住进行筛选。

我们可以通过创建一个自定义模型管理器来改进这种行为,以便在后台自动应用过滤器。如果您以前使用过 Django,您可能熟悉类似这样的语句:MyModel.objects.all().objects语句中的部分就是管理器。管理器充当 Django 代码和数据库之间的“桥梁”。它们控制对其“管理”的表执行的数据库操作。

我们新的自定义管理器可以定义为:

class SoftDeleteManager(models.Manager):

    def get_queryset(self):
        return super().get_queryset().filter(is_deleted=False)
Enter fullscreen mode Exit fullscreen mode

我们需要将新的管理器添加到我们的SoftDeleteModel基础类中:

class SoftDeleteModel(models.Model):

    is_deleted = models.BooleanField(default=False)
    objects = models.Manager()
    undeleted_objects = SoftDeleteManager()

    def soft_delete(self):
        self.is_deleted = True
        self.save()

    def restore(self):
        self.is_deleted = False
        self.save()

    class Meta:
        abstract = True
Enter fullscreen mode Exit fullscreen mode

objects请注意,由于我们已向类中添加了自定义管理器,因此我们也需要显式地添加默认管理器。

然后,我们可以简单地将查询重写为:

Note.undeleted_objects.all()
Enter fullscreen mode Exit fullscreen mode

获取QuerySet未删除的笔记列表。

我们仍然可以使用

Note.objects.all()
Enter fullscreen mode Exit fullscreen mode

获取完整的笔记列表,包括已被软删除的笔记。

处理外键关系

那么,如果有多个用户,而你想获取某个特定用户的所有笔记,该怎么办呢?最简单的办法就是编写一个查询语句,根据用户进行筛选:

Note.objects.filter(user=john, is_deleted=False)
Enter fullscreen mode Exit fullscreen mode

然而,更优雅、更易读的解决方案是利用Django 为此提供的反向关系。

john.notes.all()
Enter fullscreen mode Exit fullscreen mode

尝试软删除一些笔记,然后运行此查询。您是否注意到结果有任何异常?

我们发现结果中QuerySet包含了我们已软删除的记录。这是因为 Django 使用的是默认的objects记录管理器来执行反向查找,而您可能还记得,该管理器不会过滤掉已软删除的记录。

如何强制 Django 使用我们自定义的SoftDeleteManager反向查找表?我们可以简单地替换默认的objects查找表管理器SoftDeleteModel

class SoftDeleteModel(models.Model):

    is_deleted = models.BooleanField(default=False)
    objects = SoftDeleteManager()
    all_objects = models.Manager()

    def soft_delete(self):
        self.is_deleted = True
        self.save()

    def restore(self):
        self.is_deleted = False
        self.save()

    class Meta:
        abstract = True
Enter fullscreen mode Exit fullscreen mode

现在,objects管理器会在查询数据库时自动过滤掉软删除的对象,确保它们在任何情况下都不会泄露到我们的应用程序中!如果需要,我们仍然可以利用all_objects管理器将软删除的对象包含在查询中。

Note.all_objects.all()
Enter fullscreen mode Exit fullscreen mode

存储更多信息

我们的 Django 应用中已经有一个相当完善的软删除框架,但还可以进行最后的改进。知道一条记录是否被软删除固然有用,但如果能知道记录被软删除的时间deleted_at就更好了。为此,我们可以向我们的框架添加一个新的属性SoftDeleteModel

deleted_at = models.DateTimeField(null=True, default=None)
Enter fullscreen mode Exit fullscreen mode

我们还可以按如下方式更新我们的soft_delete方法restore

def soft_delete(self):
    self.deleted_at = timezone.now()
    self.save()

def restore(self):
    self.deleted_at = None
    self.save()
Enter fullscreen mode Exit fullscreen mode

对于未删除的记录,该值为deleted_at空;而对于软删除的记录,该值为删除的日期和时间。

由于新增的deleted_at属性使得我们之前创建的is_deleted属性变得多余,因为我们可以简单地对其进行空值检查,deleted_at以确定记录是否已被软删除。

我们重写SoftDeleteModel后的代码如下:

class SoftDeleteModel(models.Model):

    deleted_at = models.DateTimeField(null=True, default=None)
    objects = SoftDeleteManager()
    all_objects = models.Manager()

    def soft_delete(self):
        self.deleted_at = timezone.now()
        self.save()

    def restore(self):
        self.deleted_at = None
        self.save()

    class Meta:
        abstract = True
Enter fullscreen mode Exit fullscreen mode

我们重写后的SoftDeleteManager内容如下:

class SoftDeleteManager(models.Manager):

    def get_queryset(self):
        return super().get_queryset().filter(deleted_at__isnull=True)
Enter fullscreen mode Exit fullscreen mode

除了之前的功能外,我们现在还可以看到记录被软删除的确切日期和时间:

my_note.deleted_at
Enter fullscreen mode Exit fullscreen mode

综上所述

软删除是一种强大的软件模式,具有诸多优点,包括更好的数据保存和恢复、历史记录跟踪以及从故障中更快恢复。

同时,也应谨慎使用。敏感和个人数据,包括支付相关信息,应始终彻底删除。用户应始终有权选择永久删除其数据。世界各地多个司法管辖区都制定了包含“被遗忘权”的信息隐私和数据保护法律,例如欧盟的《通用数据保护条例》(GDPR)。定期删除或存档非常陈旧的数据也可能是明智之举,以避免占用过多的数据库存储空间。

如果您想查看本教程中使用的示例的完整源代码,可以在 GitHub 上找到。

图片来自PixabayMakalu。

参考文献及延伸阅读

如何在 Django 中使用管理器?
什么是软删除,以及如何实现软删除?
Django 中的软删除

文章来源:https://dev.to/bikramjeetsingh/soft-deletes-in-django-a9j