我对 Django 非常熟悉,但我最近注意到模型中有一个 on_delete=models.CASCADE
选项。我已经搜索了相同的文档,但除了以下之外我找不到任何东西:
在 Django 1.9 中进行了更改:on_delete 现在可以用作第二个位置参数(以前它通常只作为关键字参数传递)。这将是 Django 2.0 中的必需参数。
from django.db import models
class Car(models.Model):
manufacturer = models.ForeignKey(
'Manufacturer',
on_delete=models.CASCADE,
)
# ...
class Manufacturer(models.Model):
# ...
pass
on_delete 是做什么的? (我猜如果模型被删除了要执行的操作。)
models.CASCADE
有什么作用? (文档中的任何提示)
还有哪些其他选项可用(如果我的猜测是正确的)?
这方面的文档在哪里?
on_delete=models.DELETE
是做什么的?
这是删除引用对象时要采用的行为。它不是特定于 Django 的;这是一个 SQL 标准。尽管 Django 在 SQL 之上有自己的实现。 (1)
发生此类事件时,有七种可能采取的措施:
CASCADE:当引用的对象被删除时,也删除引用它的对象(例如,当您删除博客文章时,您可能也想删除评论)。 SQL 等效项:CASCADE。
PROTECT:禁止删除被引用的对象。要删除它,您必须手动删除所有引用它的对象。 SQL 等效项:RESTRICT。
RESTRICT:(在 Django 3.1 中引入)与 PROTECT 类似的行为,更准确地匹配 SQL 的 RESTRICT。 (参见 django 文档示例)
SET_NULL:将引用设置为NULL(要求该字段可以为空)。例如,当您删除用户时,您可能希望保留他在博客文章中发布的评论,但说它是由匿名(或已删除)用户发布的。 SQL 等效项:SET NULL。
SET_DEFAULT:设置默认值。 SQL 等效项:SET DEFAULT。
SET(...):设置给定值。这不是 SQL 标准的一部分,完全由 Django 处理。
DO_NOTHING:可能是一个非常糟糕的主意,因为这会在您的数据库中产生完整性问题(引用实际上不存在的对象)。 SQL 等效项:无操作。 (2)
例如,另请参见 the documentation of PostgreSQL。
在大多数情况下,CASCADE
是预期行为,但对于每个 ForeignKey,您应该始终问自己在这种情况下的预期行为是什么。 PROTECT
和 SET_NULL
通常很有用。将 CASCADE
设置在不应该的位置,可能会级联删除所有数据库,只需删除单个用户即可。
澄清级联方向的附加说明
有趣的是,很多人并不清楚 CASCADE
操作的方向。实际上,有趣的是,只有 CASCADE
操作不清楚。我知道级联行为可能会令人困惑,但是您必须认为它与任何其他操作的方向相同。因此,如果您觉得 CASCADE
的方向对您来说不清楚,那实际上意味着您对 on_delete
的行为不清楚。
在您的数据库中,外键基本上由一个整数字段表示,该字段的值是外对象的主键。假设您有一个条目 comment_A,它具有条目 article_B 的外键。如果您删除条目 comment_A,一切都很好。 article_B 过去常常没有 comment_A,即使它被删除也不必担心。但是,如果您删除 article_B,那么 comment_A 就会出现恐慌!它永远不会没有 article_B 并且需要它,它是其属性的一部分(article=article_B
,但 article_B 是什么???)。这是 on_delete
介入的地方,以确定如何解决此完整性错误,方法是:
“不!求你了!不要!没有你我活不下去!” (在 Django/SQL 中称为 PROTECT 或 RESTRICT)
“好吧,如果我不是你的,那么我就不是任何人的”(即 SET_NULL)
“再见世界,我不能没有文章_B”然后自杀(这是CASCADE行为)。
“没关系,我有多余的爱人,从现在开始我会参考article_C”(SET_DEFAULT,甚至SET(...))。
“我无法面对现实,我会一直呼唤你的名字,即使那是我唯一的事情!” (没做什么)
我希望它使级联方向更清晰。 :)
脚注
(1) Django 在 SQL 之上有自己的实现。而且,正如@JoeMjr2 在下面的评论中所提到的,Django 不会创建 SQL 约束。如果您希望数据库确保约束(例如,如果您的数据库被另一个应用程序使用,或者如果您不时挂在数据库控制台中),您可能需要自己手动设置相关约束。在 Django 中添加对删除约束的数据库级支持的开放票。
(2) 实际上,在一种情况下 DO_NOTHING 可能有用:如果您想跳过 Django 的实现并在数据库级别自己实现约束。
on_delete
方法用于告诉 Django 如何处理依赖于您删除的模型实例的模型实例。 (例如 ForeignKey
关系)。 on_delete=models.CASCADE
告诉 Django 级联删除效果,即继续删除依赖模型。
这是一个更具体的例子。假设您有一个 Author
模型,它是 Book
模型中的 ForeignKey
。现在,如果您删除 Author
模型的一个实例,Django 将不知道如何处理依赖于该 Author
模型实例的 Book
模型实例。 on_delete
方法告诉 Django 在这种情况下要做什么。设置 on_delete=models.CASCADE
将指示 Django 级联删除效果,即删除所有依赖于您删除的 Author
模型实例的 Book
模型实例。
注意:on_delete
将成为 Django 2.0 中的必需参数。在旧版本中,它默认为 CASCADE
。
Here's the entire official documentation.
仅供参考,模型中的 on_delete
参数与听起来相反。您将 on_delete
放在模型上的外键 (FK) 上,以告诉 Django 如果您在记录中指向的 FK 条目被删除,该怎么办。我们商店使用最多的选项是 PROTECT
、CASCADE
和 SET_NULL
。以下是我想出的基本规则:
当您的 FK 指向一个确实不应该更改并且肯定不应该导致您的表更改的查找表时,请使用 PROTECT。如果有人试图删除该查找表上的条目,如果该条目与任何记录相关联,PROTECT 将阻止他们将其删除。它还可以防止 Django 仅仅因为它删除了查找表上的条目而删除您的记录。最后一部分很关键。如果有人要从我的 Gender 表中删除性别“女性”,我当然不希望它立即删除我的 Person 表中具有该性别的任何人和所有人。当您的 FK 指向“父”记录时使用 CASCADE。因此,如果一个人可以有许多 PersonEthnicity 条目(他/她可以是美洲印第安人、黑人和白人),并且该人被删除,我真的希望删除任何“子”PersonEthnicity 条目。没有人,它们是无关紧要的。如果您确实希望允许人们删除查找表上的条目,但仍希望保留您的记录,请使用 SET_NULL。例如,如果一个人可以有一个高中,但如果那个高中在我的查找表上消失对我来说并不重要,我会说 on_delete=SET_NULL。这会将我的个人记录留在外面;它只会将我的 Person 上的高中 FK 设置为空。显然,您必须在该 FK 上允许 null=True。
这是一个完成所有三件事的模型示例:
class PurchPurchaseAccount(models.Model):
id = models.AutoField(primary_key=True)
purchase = models.ForeignKey(PurchPurchase, null=True, db_column='purchase', blank=True, on_delete=models.CASCADE) # If "parent" rec gone, delete "child" rec!!!
paid_from_acct = models.ForeignKey(PurchPaidFromAcct, null=True, db_column='paid_from_acct', blank=True, on_delete=models.PROTECT) # Disallow lookup deletion & do not delete this rec.
_updated = models.DateTimeField()
_updatedby = models.ForeignKey(Person, null=True, db_column='_updatedby', blank=True, related_name='acctupdated_by', on_delete=models.SET_NULL) # Person records shouldn't be deleted, but if they are, preserve this PurchPurchaseAccount entry, and just set this person to null.
def __unicode__(self):
return str(self.paid_from_acct.display)
class Meta:
db_table = u'purch_purchase_account'
最后一点,您是否知道如果您不指定 on_delete
(或没有),默认行为是 CASCADE
?这意味着,如果有人删除了您 Gender 表中的性别条目,那么任何具有该性别的 Person 记录也会被删除!
我会说,“如果有疑问,请设置 on_delete=models.PROTECT
。”然后去测试你的应用程序。您将很快找出哪些 FK 应该被标记为其他值,而不会危及您的任何数据。
此外,值得注意的是 on_delete=CASCADE
实际上并未添加到您的任何迁移中,如果这是您选择的行为。我想这是因为它是默认设置,所以放置 on_delete=CASCADE
与什么都不放置是一样的。
如前所述,CASCADE 将删除具有外键的记录并引用另一个已删除的对象。例如,如果您有一个房地产网站并且有一个引用城市的属性
class City(models.Model):
# define model fields for a city
class Property(models.Model):
city = models.ForeignKey(City, on_delete = models.CASCADE)
# define model fields for a property
现在,当从数据库中删除城市时,所有关联的属性(例如,位于该城市的房地产)也将从数据库中删除
现在我还想提一下其他选项的优点,例如 SET_NULL 或 SET_DEFAULT 甚至 DO_NOTHING。基本上,从管理的角度来看,您想要“删除”这些记录。但你真的不希望它们消失。出于很多原因。有人可能不小心删除了它,或者是为了审计和监控。和简单的报告。因此,它可以成为一种将房产与城市“断开”的方式。同样,这将取决于您的应用程序是如何编写的。
例如,某些应用程序有一个“已删除”字段,即 0 或 1。它们的所有搜索和列表视图等,任何可能出现在报告中或用户可以从前端访问的任何地方,都排除任何 {1 }。但是,如果您创建自定义报告或自定义查询来拉下已删除记录的列表,甚至可以查看上次修改的时间(另一个字段)以及由谁(即谁以及何时删除)。从执行的角度来看,这是非常有利的。
并且不要忘记,您可以像 deleted = 0
一样简单地恢复这些记录的意外删除。
我的观点是,如果有一个功能,它背后总是有原因的。并不总是一个很好的理由。而是一个原因。而且通常也很好。
使用 CASCADE 意味着实际上告诉 Django 删除引用的记录。在下面的投票应用示例中:当“问题”被删除时,它也会删除该问题的选项。
例如 问题:您是如何得知我们的? (选择:1.好友 2.电视广告 3.搜索引擎 4.邮件推广)
当您删除此问题时,它也会从表中删除所有这四个选项。注意它流动的方向。您不必将 on_delete=models.CASCADE 放入 Question Model 中,将其放入 Choice 中。
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.dateTimeField('date_published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_legth=200)
votes = models.IntegerField(default=0)
简单地说,on_delete 是一条指令,用于指定在删除外来对象的情况下将对对象进行哪些修改:
CASCADE:当外物被删除时会移除子对象
SET_NULL:将子对象外键设置为null
SET_DEFAULT:将子对象设置为创建模型时给定的默认数据
RESTRICT:引发 RestrictedError
under certain conditions。
PROTECT:防止外来对象被删除,只要有从它继承的子对象
附加链接:
https://docs.djangoproject.com/en/4.0/ref/models/fields/#foreignkey
这是您的问题的答案:为什么我们使用 on_delete?
当 ForeignKey 引用的对象被删除时,Django 默认模拟 SQL 约束 ON DELETE CASCADE 的行为,并删除包含 ForeignKey 的对象。可以通过指定 on_delete 参数来覆盖此行为。例如,如果您有一个可为空的 ForeignKey,并且您希望在删除引用的对象时将其设置为 null:
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
on_delete 的可能值在 django.db.models 中找到:
CASCADE:级联删除;默认值。
PROTECT:通过引发 django.db.IntegrityError 的子类 ProtectedError 来防止删除引用的对象。
SET_NULL:设置 ForeignKey 为空;这只有在 null 为 True 时才有可能。
SET_DEFAULT:将 ForeignKey 设置为其默认值;必须设置 ForeignKey 的默认值。
假设您有两个模型,一个名为 Person,另一个名为 Companies。
根据定义,一个人可以创建多个公司。
考虑到一家公司可以只有一个人,我们希望当一个人被删除时,与该人关联的所有公司也被删除。
所以,我们首先创建一个 Person 模型,像这样
class Person(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=20)
def __str__(self):
return self.id+self.name
然后,Company 模型看起来像这样
class Companies(models.Model):
title = models.CharField(max_length=20)
description=models.CharField(max_length=10)
person= models.ForeignKey(Person,related_name='persons',on_delete=models.CASCADE)
请注意模型公司中 on_delete=models.CASCADE
的用法。也就是说,当拥有它的人(类 Person 的实例)被删除时,将删除所有公司。
通过考虑将 FK 添加到已经存在的级联(即瀑布)来重新调整您对“CASCADE”功能的心理模型。此瀑布的来源是主键 (PK)。删除流向下。
因此,如果您将 FK 的 on_delete 定义为“CASCADE”,则您将此 FK 的记录添加到源自 PK 的级联删除中。 FK 的记录可能参与或不参与此级联(“SET_NULL”)。事实上,带有 FK 的记录甚至可以阻止删除流程!用“PROTECT”建造水坝。
删除父对象时删除数据库中的所有子字段,然后我们使用 on_delete 如下:
class user(models.Model):
commodities = models.ForeignKey(commodity, on_delete=models.CASCADE)
CASCADE 也会删除与之相连的相应字段。
不定期副业成功案例分享
Comment
有一个指向BlogPost
的外键,那么删除 BlogPost 应该删除 Comment,但删除 Comment 不应该删除 BlogPost,不管 RDMS 是什么?Comment
,他的表中有 FK 字段,而如果我们谈论真实模型,BlogPost
“拥有”Comment
。好的。