这将从我的应用程序中的实现中总结出解决方案。一些代码是LWN 的答案。
有 4 种情况会删除您的数据:
- SQL查询
- 调用
delete()
模型实例:project.delete()
- 调用
delete()
QuerySet 实例:Project.objects.all().delete()
- 被其他模型上的 ForeignKey 字段删除
虽然您对第一种情况无能为力,但可以对其他三种情况进行细粒度控制。一个建议是,在大多数情况下,您永远不应该删除数据本身,因为这些数据反映了我们应用程序的历史和使用情况。active
而是首选设置布尔字段。
为了防止delete()
模型实例,delete()
在您的模型声明中进行子类化:
def delete(self):
self.active = False
self.save(update_fields=('active',))
而delete()
在 QuerySet 实例上,需要像LWN 的回答那样使用自定义对象管理器进行一些设置。
将其包装为可重用的实现:
class ActiveQuerySet(models.QuerySet):
def delete(self):
self.save(update_fields=('active',))
class ActiveManager(models.Manager):
def active(self):
return self.model.objects.filter(active=True)
def get_queryset(self):
return ActiveQuerySet(self.model, using=self._db)
class ActiveModel(models.Model):
""" Use `active` state of model instead of delete it
"""
active = models.BooleanField(default=True, editable=False)
class Meta:
abstract = True
def delete(self):
self.active = False
self.save()
objects = ActiveManager()
用法,只是子ActiveModel
类:
class Project(ActiveModel):
...
如果它的任何一个 ForeignKey 字段被删除,我们的对象仍然可以被删除:
class Employee(models.Model):
name = models.CharField(name, unique=True)
class Project(models.Model):
name = models.CharField(name, unique=True)
manager = purchaser = models.ForeignKey(
Employee, related_name='project_as_manager')
>>> manager.delete() # this would cause `project` deleted as well
这可以通过添加模型字段的on_delete 参数来防止:
class Project(models.Model):
name = models.CharField(name, unique=True)
manager = purchaser = models.ForeignKey(
Employee, related_name='project_as_manager',
on_delete=models.PROTECT)
默认 ofon_delete
将CASCADE
导致您的实例被删除,而使用PROTECT
它会引发一个ProtectedError
(的子类IntegrityError
)。这样做的另一个目的是要保留数据的 ForeignKey 作为参考。