on_delete对Django模型有什么作用?

我对Django非常熟悉,但是我最近注意到模型中存在一个on_delete=models.CASCADE选项。我已经搜索了相同的文档,但我找不到更多的东西:

在Django 1.9中更改:

on_delete现在可以用作第二个位置参数(以前它通常只作为关键字参数传递)。它将是Django 2.0中必须的参数。

# EYZ0:

from django.db import models


class Car(models.Model):
manufacturer = models.ForeignKey(
'Manufacturer',
on_delete=models.CASCADE,
)
# ...


class Manufacturer(models.Model):
# ...
pass

on_delete做什么?(# EYZ0)。

models.CASCADE做什么?(# EYZ1)

还有什么其他选项可用(如果我猜对了)?

这方面的文档放在哪里?

443440 次浏览

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

这是完整的官方文档

这是删除引用对象时采用的行为。它不是Django特有的;这是一个SQL标准。尽管Django在SQL之上有自己的实现。# EYZ1

当此类事件发生时,可以采取7种措施:

  • CASCADE:当被引用的对象被删除时,也要删除对它有引用的对象(例如,当你删除一篇博客文章时,你可能也想删除评论)。SQL等价:CASCADE
  • PROTECT:禁止删除引用对象。要删除它,必须手动删除所有引用它的对象。SQL等价:RESTRICT
  • RESTRICT: (在Django 3.1中引入)PROTECT类似的行为,更准确地匹配SQL的RESTRICT。(见# EYZ4)
  • SET_NULL:将引用设置为NULL(要求字段可以为空)。例如,当您删除一个User时,您可能希望保留他在博客文章上发布的评论,但说它是由匿名(或已删除)用户发布的。SQL等价:SET NULL
  • SET_DEFAULT:设置默认值。SQL等价:SET DEFAULT
  • SET(...):设置一个给定值。这不是SQL标准的一部分,完全由Django处理。
  • DO_NOTHING:这可能是一个非常糟糕的主意,因为这会在数据库中产生完整性问题(引用一个实际上不存在的对象)。SQL等价:NO ACTION。# EYZ2

来源:# EYZ0

例如,请参见PostgreSQL的文档

在大多数情况下,CASCADE是预期的行为,但对于每个ForeignKey,你应该总是问自己在这种情况下预期的行为是什么。PROTECTSET_NULL通常很有用。在不应该设置CASCADE的地方设置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介入的地方,决定如何解决on_delete1,要么说:

  • “不!拜托!不!没有你我活不下去!(在Django/SQL中是PROTECTRESTRICT)
  • “好吧,如果我不是你的,那我谁都不是。”(也就是SET_NULL)
  • “再见世界,我不能没有文章”;和自杀(这是CASCADE行为)。
  • “没关系,我有备用情人了,从现在起我会参考article_C”; (SET_DEFAULT,甚至SET(...))。
  • # EYZ1 (# EYZ0)

我希望它能让瀑布方向更清晰。:)


脚注

Django在SQL之上有自己的实现。并且,在@JoeMjr2在下面的评论中提到中,Django不会创建SQL约束。如果您希望数据库确保约束(例如,如果您的数据库由另一个应用程序使用,或者您不时挂在数据库控制台中),您可能希望自己手动设置相关的约束。在Django中添加了不定期机票来支持数据库级别的删除约束。

实际上,有一种情况DO_NOTHING是有用的:如果你想跳过Django的实现,自己在数据库级实现约束。

供您参考,模型中的on_delete参数与它听起来的样子相反。你把on_delete放在一个模型的外键(FK)上,告诉Django如果你在记录上指向的FK条目被删除了该怎么做。我们商店使用最多的选项是PROTECTCASCADESET_NULL。以下是我总结出的基本规则:

  1. 当FK指向一个不应该更改的查找表,并且当然不应该导致表更改时,使用PROTECT。如果有人试图删除查找表中的一个条目,PROTECT将阻止他们删除它,如果它绑定到任何记录。它还可以防止Django删除你的记录,因为它删除了查找表中的一个条目。最后一部分非常关键。# EYZ4
  2. 当FK指向“parent”时,使用CASCADE;记录。因此,如果一个Person可以有许多PersonEthnicity条目(他/她可以是美洲印第安人,黑人和白人),并且Person 删除了,我真的想要任何“;孩子”;要删除的PersonEthnicity条目。如果没有人格,它们就无关紧要了。
  3. 当您希望允许人们删除查找表中的条目,但仍然希望保留您的记录时,请使用SET_NULL。例如,如果一个人可以有一个高中,但如果这个高中在我的查询表上消失了,对我来说并不重要,我会说on_delete=SET_NULL。这将会留下我的Person记录;它会把Person上的high FK设为null。显然,你必须允许在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。基本上,从管理的角度来看,您希望“删除”这些记录。但你不希望它们消失。原因有很多。有人可能不小心删除了它,或者是为了审计和监控。以及简单的报道。因此,这可能是一种将房产与城市“断开”的方法。同样,这取决于您的应用程序是如何编写的。

例如,一些应用程序的字段“deleted”为0或1。他们所有的搜索和列表视图等,任何可以出现在报告或任何用户可以从前端访问它的地方,排除任何deleted == 1的东西。然而,如果你创建一个自定义报告或自定义查询下拉被删除的记录列表,甚至可以查看它最后一次被修改的时间(另一个字段)以及由谁(即谁在什么时候删除了它)。从执行的角度来看,这是非常有利的。

不要忘记,您可以恢复这些记录的意外删除,简单到deleted = 0

我的观点是,如果有一个功能,它背后总是有一个原因。这并不总是一个好理由。但是有一个原因。而且通常都是很好的。

这是你的问题的答案,说:为什么我们使用on_delete?

当一个被ForeignKey引用的对象被删除时,Django默认会模仿SQL约束ON DELETE CASCADE的行为,同时也会删除包含ForeignKey的对象。可以通过指定on_delete参数来重写此行为。例如,如果你有一个可为空的ForeignKey,并且你想在删除引用对象时将它设置为空:

user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)

on_delete的可能值可以在django.db.models中找到:

级联:级联删除;默认值。

通过引发django.db.IntegrityError的子类ProtectedError来防止被引用对象的删除。

设置ForeignKey为空;这只有在null为True时才有可能。

设置ForeignKey为默认值;必须设置一个默认的ForeignKey。

使用级联实际上意味着告诉Django删除引用的记录。 在下面的投票应用示例中:当一个“问题”被删除时,它也会删除这个问题的选项 < p > e。问:你是怎么知道我们公司的? (选择:1。朋友2。电视广告搜索引擎4。电子邮件推广)< / p >

当你删除这个问题时,它也会从表中删除所有这四个选项。 注意它流动的方向。 你不需要输入on_delete=models。

.

.
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)

假设您有两个模型,一个名为,另一个名为公司

根据定义,一个人可以创建多家公司。

考虑到一个公司可以有且只能有一个人,我们希望当一个人被删除时,与他相关的所有公司也都被删除。

我们首先创建一个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

然后,Companies模型看起来像这样

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)

注意模型Companies中on_delete=models.CASCADE的使用。也就是说,当拥有公司的人(类person的实例)被删除时,删除所有公司。

重新定位你对“级联”功能的思维模式;通过考虑将FK添加到已经存在的级联(即瀑布)。这个瀑布的源是一个主键(PK)。删除向下流动。

因此,如果你将一个FK的on_delete定义为“CASCADE”;您正在将这个FK的记录添加到一个来自PK的级联删除中。FK的记录可能参与这个级联,也可能不参与("SET_NULL")事实上,具有FK的记录甚至可能阻止删除流!用“PROTECT”建造大坝;

CASCADE也会删除与之相连的对应字段。

删除父对象时删除数据库中的所有孩子字段,然后我们使用on_delete,如下所示:

class user(models.Model):
commodities = models.ForeignKey(commodity, on_delete=models.CASCADE)

简单地说,on_delete是一条指令,用于指定在删除外部对象时将对对象进行哪些修改:

级联:当外部对象被删除时,将删除子对象

SET_NULL:将子对象外键设置为null

SET_DEFAULT:将在创建模型时将子对象设置为默认数据

限制:引发RestrictedError 在某些条件下

保护:阻止外部对象被删除,只要有子对象从它继承

额外的链接:

https://docs.djangoproject.com/en/4.0/ref/models/fields/#foreignkey