on_delete对Django模型有什么作用?


346

我对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办?(文档中的任何提示

还有其他可用的选项(如果我的猜测是正确的)?

有关此文档的位置在哪里?



1
现在,在下面的此答案中列出了类似问题的文字。它开始于“ FYI,模型中的on_delete参数从听起来像是向后的。” 它提供了比原始答案更多的细节。
HelenM '17

Answers:


774

这是删除引用对象时采取的行为。它不是特定于Django的,这是一种SQL标准。

发生此类事件时,有6种可能的操作:

  • CASCADE:删除引用的对象时,还请删除引用了该对象的对象(例如,删除博客文章时,您可能还希望删除注释)。SQL等效项:CASCADE
  • PROTECT:禁止删除引用的对象。要删除它,您将必须删除所有手动引用它的对象。SQL等效项:RESTRICT
  • SET_NULL:将引用设置为NULL(要求该字段可为空)。例如,当删除用户时,您可能希望保留他在博客文章中发布的评论,但说该评论是由匿名(或已删除)用户发布的。SQL等效项:SET NULL
  • SET_DEFAULT:设置默认值。SQL等效项:SET DEFAULT
  • SET(...):设置给定值。这不是SQL标准的一部分,完全由Django处理。
  • DO_NOTHING:这可能是一个非常糟糕的主意,因为这会在数据库中造成完整性问题(引用实际上不存在的对象)。SQL等效项:NO ACTION

资料来源:Django说明文件

例如,另请参阅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确定如何解决此完整性错误的步骤,或者说:

  • “不!请!不要!我不能没有你!” PROTECT用SQL语言表示)
  • “好吧,如果我不是你的,那我就不是任何人的”(说SET_NULL
  • “再见,我不能没有article_B生活”自杀(这是CASCADE行为)。
  • “没关系,我有多余的恋人,从现在开始我将引用article_C”SET_DEFAULT,甚至SET(...))。
  • “我不能面对现实,即使那是我唯一的事情,我也会继续给你起名字!” DO_NOTHING

我希望它使级联方向更清晰。:)


19
一个愚蠢的问题,但是级联应该始终是一个方向吧?即如果Comment 具有外键,BlogPost则删除BlogPost应该删除评论,但是删除RD不应删除BlogPost,而不管RDMS是什么?
安东尼·曼宁-富兰克林

20
@AnthonyManningFranklin当然。仅当引用“断开”时才触发删除。当您删除评论时,情况并非如此,因为您同时删除了引用。
安托万·皮萨德

6
这个问题并不愚蠢。我也需要那个解释。因此,在这里我们假设关系是单边的,关系的所有者是Comment,在表中具有FK字段,而如果我们谈论现实生活模型,则BlogPost“所有者” Comment是。好。
WesternGun

3
需要注意的重要一点是,在Django中设置on_delete不会在数据库本身中创建ON D​​ELETE子句。指定的行为(例如CASCADE)将仅影响通过Django执行的删除,而不影响直接在数据库中直接进行的原始删除。
JoeMjr2

2
很好的解释。得到我的支持!
Homunculus Reticulli

42

on_delete方法用于告诉Django如何处理依赖于您删除的模型实例的模型实例。(例如,ForeignKey恋爱关系)。该命令on_delete=models.CASCADE告诉Django级联删除效果,即也继续删除相关模型。

这是一个更具体的例子。假设您有一个Author模型ForeignKey中的一个Book模型。现在,如果删除Author模型实例,则Django将不知道如何处理Book依赖于该Author模型实例的模型实例。该on_delete方法告诉Django在这种情况下该怎么做。设置on_delete=models.CASCADE将指示Django级联删除效果,即删除所有Book依赖于Author您删除的模型实例的模型实例。

注意:on_delete在Django 2.0中将成为必填参数。在旧版本中,默认为CASCADE

这是完整的官方文档。


37

仅供参考,on_delete模型中的参数从听起来像是倒过来的。您on_delete在模型上放置了外键(FK),以告诉django如果删除了记录中指向的FK条目该怎么办。选项我们店已经使用的大多是PROTECTCASCADESET_NULL。这是我弄清楚的基本规则:

  1. 使用PROTECT时,你的FK指向一个查表真的不应该被改变,并且肯定不会引起你的表来改变。如果有人试图删除该查询表上的条目,则PROTECT防止该条目与任何记录绑定时删除该条目。它还可以防止从删除的Django 你的记录,只是因为它删除了一个查找表中的条目。最后一部分至关重要。 如果有人要从“性别”表中删除性别“女性”,我肯定不希望立即删除我在“人”表中拥有该性别的任何人。
  2. 使用CASCADE时,你的FK指向“父”的纪录。所以,如果一个人可以有很多PersonEthnicity项(他/她可以是美洲印第安人,黑色和白色),而那个人删除了,我真的想什么“孩子” PersonEthnicity条目被删除。没有人,他们是无关紧要的。
  3. 使用SET_NULL时,你希望人们被允许删除查找表中的条目,但你仍然要保留记录。例如,如果某人可以拥有一所高中,但对我而言,那所高中不在我的查询表上并不重要on_delete=SET_NULL。这会将我的“个人”记录保留在那里;只会将“我的人”上的高中FK设置为​​null。显然,您必须允许null=True该FK。

这是一个可以完成所有三件事的模型示例:

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?这意味着,如果有人删除了您“性别”表上的性别条目,则具有该性别的任何“人”记录也将被删除!

我会说:“如果有疑问,那就出发on_delete=models.PROTECT。” 然后测试您的应用程序。您将快速找出哪些FK应该标记为其他值,而不会危及您的任何数据。

另外,值得注意的on_delete=CASCADE是,如果这是您选择的行为,实际上并没有添加到您的任何迁移中。我猜这是因为它是默认设置,所以放置on_delete=CASCADE和放置任何东西都是一样的。


12

如前所述,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。所有搜索和列表视图等内容,可能出现在报表中或用户可以从前端访问它的任何位置,均不包括deleted == 1。但是,如果您创建自定义报告或自定义查询来下拉已删除记录的列表,甚至更多,以便查看上次修改的时间(另一个字段)以及由谁(即谁删除它和何时删除)。从行政角度来看,这是非常有利的。

并且不要忘记,您可以像还原deleted = 0那些记录一样简单地还原意外删除。

我的观点是,如果有功能,总会有其背后的原因。并非总是一个很好的理由。但这是一个原因。往往也是一个好人。


3
这很有帮助,因为它阐明了CASCADE发生的方向。如果您不熟悉SQL级联,则可接受的答案尚不清楚。
codecribblr

谢谢:)万分感谢!
乔治·莫吉廖夫斯基

2
我赞成这个答案,因为它回答了我对关系模型发展方向的怀疑
edepe

6

这是您的问题答案:为什么我们使用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中找到:

级联级联删除;默认值。

保护:通过引发ProtectedError(django.db.IntegrityError的子类)来防止删除引用的对象。

SET_NULL:将ForeignKey设置为null;否则为false。仅当null为True时才有可能。

SET_DEFAULT:将ForeignKey设置为其默认值;必须为ForeignKey设置默认值。


简单的话对我来说很清楚,因为我还不熟悉sql和django。谢谢。
wm.p1us

2

假设您有两种模型,一种名为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

然后,公司模型如下所示

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类的实例)时删除所有公司。


1

通过考虑将FK添加到已存在的级联(即瀑布)中来重新定向“ CASCADE”功能的思维模型。该瀑布的来源是主键。删除流向下。

因此,如果将FK的on_delete定义为“ CASCADE”,则需要将此FK的记录添加到源自PK的一系列删除中。FK的记录是否可以参与此级联(“ SET_NULL”)。实际上,带有FK的记录甚至可能阻止删除流程!用“保护”建造一个水坝。


0

使用CASCADE意味着实际上告诉Django删除引用的记录。在下面的民意调查应用示例中:当“问题”被删除时,它还将删除该问题具有的选择。

例如:问题:您如何得知我们的?(选择:1.朋友2.电视广告3.搜索引擎4.电子邮件促销)

删除此问题时,它还将从表中删除所有这四个选项。 请注意它流动的方向。您不必放置on_delete = models。问题模型中的CASCADE将其放置在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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.