我很熟悉Django,但我最近注意到存在一个on_delete=models。级联选项与模型。我已经搜索了相同的文档,但我找不到更多的东西:

在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做什么?(我猜如果模型被删除了要做的动作。)

什么是模型。级联做什么?(文档中的提示)

还有什么其他的选择(如果我的猜测是正确的)?

这方面的文档放在哪里?


on_delete方法用于告诉Django如何处理依赖于你删除的模型实例的模型实例。(例如一个ForeignKey关系)。on_delete =模型。CASCADE告诉Django级联删除效果,即继续删除依赖模型。

Here's a more concrete example. Assume you have an Author model that is a ForeignKey in a Book model. Now, if you delete an instance of the Author model, Django would not know what to do with instances of the Book model that depend on that instance of Author model. The on_delete method tells Django what to do in that case. Setting on_delete=models.CASCADE will instruct Django to cascade the deleting effect i.e. delete all the Book model instances that depend on the Author model instance you deleted.

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

这是完整的官方文件。


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

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

CASCADE: When the referenced object is deleted, also delete the objects that have references to it (when you remove a blog post for instance, you might want to delete comments as well). SQL equivalent: CASCADE. PROTECT: Forbid the deletion of the referenced object. To delete it you will have to delete all objects that reference it manually. SQL equivalent: RESTRICT. RESTRICT: (introduced in Django 3.1) Similar behavior as PROTECT that matches SQL's RESTRICT more accurately. (See django documentation example) SET_NULL: Set the reference to NULL (requires the field to be nullable). For instance, when you delete a User, you might want to keep the comments he posted on blog posts, but say it was posted by an anonymous (or deleted) user. SQL equivalent: SET NULL. SET_DEFAULT: Set the default value. SQL equivalent: SET DEFAULT. SET(...): Set a given value. This one is not part of the SQL standard and is entirely handled by Django. DO_NOTHING: Probably a very bad idea since this would create integrity issues in your database (referencing an object that actually doesn't exist). SQL equivalent: NO ACTION. (2)

来源:Django文档

例如,请参阅PostgreSQL的文档。

在大多数情况下,CASCADE是预期的行为,但对于每个ForeignKey,你应该总是问自己在这种情况下预期的行为是什么。PROTECT和SET_NULL通常很有用。在不应该的地方设置CASCADE,只需删除单个用户,就可以潜在地级联删除所有数据库。


澄清级联方向的附加说明

有趣的是,许多人都不清楚CASCADE动作的方向。实际上,有趣的是,只有CASCADE动作是不清楚的。我知道级联行为可能会让人困惑,但你必须认为它与任何其他行为的方向相同。因此,如果您觉得CASCADE方向不清楚,这实际上意味着on_delete行为不清楚。

In your database, a foreign key is basically represented by an integer field which value is the primary key of the foreign object. Let's say you have an entry comment_A, which has a foreign key to an entry article_B. If you delete the entry comment_A, everything is fine. article_B used to live without comment_A and don't bother if it's deleted. However, if you delete article_B, then comment_A panics! It never lived without article_B and needs it, it's part of its attributes (article=article_B, but what is article_B???). This is where on_delete steps in, to determine how to resolve this integrity error, either by saying:

“不!拜托!不!我不能没有你!”(在Django/SQL中是PROTECT或RESTRICT) “好吧,如果我不是你的,那我就不是任何人的”(SET_NULL) “再见世界,我不能没有article_B”然后自杀(这是CASCADE行为)。 “没关系,我有一个备用情人,我将从现在开始引用article_C”(SET_DEFAULT,甚至SET(…))。 “我无法面对现实,我会一直叫你的名字,即使那是我唯一剩下的东西!”(DO_NOTHING)

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


脚注

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

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


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

Use PROTECT when your FK is pointing to a look-up table that really shouldn't be changing and that certainly should not cause your table to change. If anyone tries to delete an entry on that look-up table, PROTECT prevents them from deleting it if it is tied to any records. It also prevents Django from deleting your record just because it deleted an entry on a look-up table. This last part is critical. If someone were to delete the gender "Female" from my Gender table, I CERTAINLY would NOT want that to instantly delete any and all people I had in my Person table who had that gender. Use CASCADE when your FK is pointing to a "parent" record. So, if a Person can have many PersonEthnicity entries (he/she can be American Indian, Black, and White), and that Person is deleted, I really would want any "child" PersonEthnicity entries to be deleted. They are irrelevant without the Person. Use SET_NULL when you do want people to be allowed to delete an entry on a look-up table, but you still want to preserve your record. For example, if a Person can have a HighSchool, but it doesn't really matter to me if that high-school goes away on my look-up table, I would say on_delete=SET_NULL. This would leave my Person record out there; it just would just set the high-school FK on my Person to null. Obviously, you will have to allow null=True on that 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吗?这意味着如果有人删除了您的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。基本上,从管理的角度来看,您希望“删除”这些记录。但你不希望它们消失。原因有很多。有人可能不小心删除了它,或者是为了审计和监控。以及简单的报道。因此,这可能是一种将房产与城市“断开”的方法。同样,这取决于您的应用程序是如何编写的。

For example, some applications have a field "deleted" which is 0 or 1. And all their searches and list views etc, anything that can appear in reports or anywhere the user can access it from the front end, exclude anything that is deleted == 1. However, if you create a custom report or a custom query to pull down a list of records that were deleted and even more so to see when it was last modified (another field) and by whom (i.e. who deleted it and when)..that is very advantageous from the executive standpoint.

不要忘记,对于这些记录,您可以恢复意外删除,只需删除= 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中找到:

CASCADE:级联删除;默认值。

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

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

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


使用CASCADE实际上意味着告诉Django删除引用的记录。 当一个“问题”被删除时,它也会删除这个问题的选项。

问:你是怎么知道我们公司的? (选择:1。朋友2。电视广告搜索引擎4。电子邮件推广)

当您删除这个问题时,它也会从表中删除所有这四个选项。 注意它流动的方向。 你不需要输入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,另一个名为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

然后,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)

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


重新定位你对“CASCADE”功能的思维模式,将FK添加到一个已经存在的CASCADE(即瀑布)中。这个瀑布的源是一个主键(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是一条指令,用于指定在删除外部对象时将对对象进行哪些修改:

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

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

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

RESTRICT:在某些条件下引发RestrictedError。

PROTECT:防止外部对象被删除,只要有子对象从它继承

额外的链接:

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