我有一个类似的设置(为这个问题简化):
class Employee(models.Model):
name = models.CharField(name, unique=True)
class Project(models.Model):
name = models.CharField(name, unique=True)
employees = models.ManyToManyField(Employee)
字符串
当一个Employee
即将被删除时,我想检查他是否连接到任何项目。如果是这样,删除就不可能了。
我知道信号和如何使用它们。我可以连接到pre_delete
信号,让它抛出一个异常,比如ValidationError
。这可以防止删除,但表单等无法正常处理。
这似乎是其他人会遇到的情况。我希望有人能指出一个更优雅的解决方案。
9条答案
按热度按时间roqulrg31#
我正在寻找这个问题的答案,但没有找到一个好的,这将适用于两个模型。Model.delete()和QuerySet.delete()。我沿着了,某种程度上,实现了史蒂夫·K的解决方案。我使用这个解决方案来确保一个对象(在本例中是Employee)不能以任何一种方式从数据库中删除,而是设置为非活动。
这是一个迟来的回答。只是为了让其他人看我把我的解决方案放在这里。
代码如下:
字符串
使用方法:
型
或者在admin中:
型
o7jaxewo2#
对于那些在
ForeignKey
关系中引用相同问题的人,正确的答案是在ForeignKey
关系中使用Djago的on_delete=models.PROTECT
字段。这将防止删除任何具有外键链接的对象。这不适用于ManyToManyField
关系(如this问题中所讨论的),但适用于ForeignKey
字段。因此,如果模型是这样的,这将防止删除任何具有一个或多个
Project
对象的Employee
对象:字符串
文档可以在这里找到。
jv2fixgn3#
这将结束从我的应用程序中的实现解决方案。有些代码是LWN's answer.
有4种情况下,您的数据被删除:
delete()
:project.delete()
个delete()
:Project.objects.all().delete()
个虽然对第一种情况没有什么可做的,但其他三种情况可以细粒度控制。一个建议是,在大多数情况下,您不应该删除数据本身,因为这些数据反映了我们应用程序的历史和使用情况。首选
active
布尔字段的设置。要在Model示例上防止
delete()
,请在Model声明中子类化delete()
:字符串
而QuerySet示例上的
delete()
需要像LWN's answer.中那样使用自定义对象管理器进行一些设置将其 Package 为可重用的实现:
型
用法,只是
ActiveModel
类的子类:型
如果对象的ForeignKey字段中的任何一个被删除,我们的对象仍然可以被删除:
型
这可以通过添加模型字段的on_delete参数来防止:
型
on_delete
的默认值是CASCADE
,这将导致您的示例被删除,而使用PROTECT
将引发ProtectedError
(IntegrityError
的子类)。这样做的另一个目的是数据的ForeignKey应该被保留作为引用。piv4azn74#
如果您知道永远不会有任何大规模的员工删除尝试,您可以在模型上覆盖
delete
,并且只在法律的操作时调用super
。不幸的是,任何可能调用
queryset.delete()
的东西都会直接转到SQL:http://docs.djangoproject.com/en/dev/topics/db/queries/#deleting-objects但我不认为这是一个大问题,因为您是编写这段代码的人,可以确保员工身上永远不会有任何
queryset.delete()
。手动调用delete()
。我希望删除员工是相对罕见的。
字符串
rqenqsqc5#
我想提出一个关于LWN和anhdat's答案的变体,其中我们使用
deleted
字段而不是active
字段,并且我们从默认查询集中排除“已删除”对象,以便将这些对象视为不再存在,除非我们特别包括它们。字符串
使用方法:
型
正如anhdat的回答中所述,确保在模型上设置ForeignKeys的
on_delete
属性,以避免级联行为,例如。型
备注:
正如我刚刚发现的,
django-model-utils
的SoftDeletableModel
中也包含类似的功能。值得一查。附带一些其他方便的东西。vsnjm48y6#
我有一个建议,但我不确定它是否比你现在的想法好。看一下here这个遥远但又不相关的问题的答案,你可以在django管理中通过删除它们并使用你自己的来覆盖各种操作。例如,他们有:
字符串
如果您不像我一样使用djangoadmin,那么在允许用户删除对象之前,只需在UI逻辑中构建检查即可。
n9vozmp47#
如果有人发现了这一点,并想知道如何将PROTECT添加到模型字段中,但让它忽略任何软删除的对象,你可以通过简单地覆盖Django自带的PROTECT来做到这一点:
字符串
这将检查是否有任何未被软删除的对象,并仅返回错误中的那些对象。这还没有优化。
kzmpq1sx8#
不敢相信我已经10年没问过这个问题了。类似的问题再次出现,我们最终将解决方案打包到内部使用的一个小工具包中。它添加了一个
ProtectedModelMixin
,这与这里提出的问题有关。参见https://github.com/zostera/django-marinavuv7lop39#
我也遇到了同样的问题,只是找到了一个很好的解决方案。
有两种方法可以尝试删除模型示例:通过删除示例或通过在整个查询集上调用delete。您不必担心有人在管理器上调用delete,因为delete方法不在管理器中公开。引用Django文档:
请注意,delete()是唯一没有在Manager本身上公开的QuerySet方法。这是一种安全机制,可以防止您意外地请求Entry.objects.delete()并删除所有条目。如果您确实想删除所有对象,则必须显式请求完整的查询集:第一个月
为了防止示例级删除,您可以简单地覆盖模型类中的delete方法:
字符串
现在来看下一个问题:查询集删除。为了解决这个问题,我们需要了解Django的一些事情:
型
因此,为了防止querysets批量删除我们的模型,我们可以使用以下代码:
型
就像这样,您已经阻止了所有可以删除模型示例的方式,并且在任何时候,每当有人试图删除它们时,您都会引发错误。
请注意,这种行为是如何通过过滤器的链接来保留的,并被排除,因为返回的每个查询集仍然是覆盖delete方法的UndeletableQuery集的示例。