что делает on_delete на моделях Django?

348

Я хорошо знаком с 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? ( любые намеки в документации )

Какие еще варианты доступны ( если мои предположения верны )?

Где находится документация для этого?

Все это Ваити
источник
Также есть ответ на подобный вопрос на stackoverflow.com/questions/47914325/…
HelenM
1
Текст этого похожего вопроса теперь указан ниже, на этот ответ. Начинается «FYI, параметр on_delete в моделях обратно по сравнению с тем, как он звучит». Это обеспечивает намного больше деталей, чем оригинальные ответы.
HelenM

Ответы:

787

Это поведение, которое нужно применять при удалении ссылочного объекта. Это не специфично для 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 следует удалить комментарий, но удаление комментария не должно удалять BlogPost вне зависимости от RDMS?
Энтони Мэннинг-Франклин
20
@AnthonyManningFranklin Конечно. При удалении активируется только тогда, когда ссылка «не работает». Это не тот случай, когда вы удаляете комментарий, так как вы удаляете ссылку одновременно.
Антуан Пинсард
6
Вопрос не глупый; Мне тоже нужно это объяснение. Таким образом, здесь мы предполагаем, что отношение является односторонним, владелец отношения - тот Comment, кто имеет поле FK в своей таблице, в то время как BlogPost«владеет», Commentесли мы говорим о реальной модели. Хорошо.
WesternGun
3
Важно отметить, что установка on_delete в Django НЕ создает предложение ON DELETE в самой базе данных. Указанное поведение (например, CASCADE) будет влиять только на удаления, выполняемые через Django, а не на необработанные удаления, сделанные непосредственно в базе данных.
JoeMjr2
2
Эти цитаты в конце выглядят так, словно их вытащили прямо из панелей комиксов Роя Лихтенштейна! Удивительно
авиаудар
42

Этот on_deleteметод используется для указания Django, что делать с экземплярами модели, которые зависят от удаляемого экземпляра модели. (например, ForeignKeyотношения). on_delete=models.CASCADEГоворит Джанго , чтобы обрушивать УДАЛЕНИЕ эффект т.е. продолжить удаление зависимых моделей , а также.

Вот более конкретный пример. Предположим, у вас есть Authorмодель, которая есть ForeignKeyв Bookмодели. Теперь, если вы удалите экземпляр Authorмодели, Django не будет знать, что делать с экземплярами Bookмодели, которые зависят от этого экземпляра Authorмодели. on_deleteМетод говорит Джанго , что делать в этом случае. Параметр on_delete=models.CASCADEдаст Django каскадный эффект удаления, т.е. удалит все Bookэкземпляры модели, которые зависят от Authorудаленного экземпляра модели.

Примечание: on_deleteстанет обязательным аргументом в Django 2.0. В старых версиях по умолчанию CASCADE.

Вот вся официальная документация.

him229
источник
37

К вашему сведению, on_deleteпараметр в моделях обратен тому, на что это похоже. Вы надеваете on_deleteвнешний ключ (FK) на модель, чтобы сообщить django, что делать, если запись FK, на которую вы указываете в своей записи, удалена. Варианты нашего магазина использовали большинство из них PROTECT, CASCADEи SET_NULL. Вот основные правила, которые я выяснил:

  1. Используйте, PROTECTкогда ваш FK указывает на справочную таблицу, которая на самом деле не должна меняться и которая, безусловно, не должна вызывать изменения вашей таблицы. Если кто-то пытается удалить запись в этой справочной таблице, он PROTECTне может удалить ее, если она связана с какими-либо записями. Это также не позволяет django удалить вашу запись только потому, что она удалила запись в справочной таблице. Эта последняя часть имеет решающее значение. Если бы кто-то удалил пол «Женщина» из моей таблицы «Гендер», Я бы НЕ хотел, чтобы это мгновенно удаляло всех и всех людей, которые были у меня в таблице «Персона», которые имели этот пол.
  2. Используйте, CASCADEкогда ваш FK указывает на «родительскую» запись. Таким образом, если у Person может быть много записей PersonEthnicity (он может быть американским индейцем, чернокожим и белым), и этот Person будет удален, я действительно хотел бы удалить любые "дочерние" записи PersonEthnicity. Они не имеют отношения к личности.
  3. Используйте, SET_NULLесли вы хотите, чтобы людям было разрешено удалить запись в справочной таблице, но вы все равно хотите сохранить свою запись. Например, если у человека может быть старшая школа, но для меня не имеет значения, уйдет ли эта старшая школа на мою таблицу поиска, я бы сказал on_delete=SET_NULL. Это оставило бы мою личную запись там; это просто установило бы для моей персоны среднюю школу FK на ноль. Очевидно, вам придется допустить 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- это то же самое, что установка ничего.

HelenM
источник
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
Это было полезно, потому что выяснило, в каком направлении происходит КАСКАД. Принятый ответ не ясен, если вы не знакомы с каскадами SQL.
codescribblr
Спасибо :) очень ценится!
Георгий Могилевский
2
Я одобряю этот ответ, потому что он отвечает на мои сомнения относительно направления в модели отношений
edepe
6

Вот ответ на ваш вопрос, который говорит: почему мы используем on_delete?

Когда объект, на который ссылается ForeignKey, удаляется, Django по умолчанию эмулирует поведение ограничения SQL ON DELETE CASCADE, а также удаляет объект, содержащий ForeignKey. Это поведение можно изменить, указав аргумент on_delete. Например, если у вас есть Nullable ForeignKey, и вы хотите, чтобы он был установлен равным NULL при удалении ссылочного объекта:

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

Возможные значения on_delete находятся в django.db.models:

КАСКАД: Каскад удаляет; по умолчанию.

PROTECT: Предотвратить удаление объекта, на который есть ссылка, подняв ProtectedError, подкласс django.db.IntegrityError.

SET_NULL: установить значение ForeignKey пустым; это возможно только в том случае, если null равен True.

SET_DEFAULT: установить ForeignKey в значение по умолчанию; значение по умолчанию для ForeignKey должно быть установлено.

Соня Рани
источник
Простые слова проясняют для меня, так как я не зрелый с sql и django. Спасибо.
wm.p1us
3

Допустим, у вас есть две модели, одна с именем 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

Переориентируйте свою ментальную модель функциональности «КАСКАД», подумав о добавлении ФК к уже существующему каскаду (то есть водопаду). Источником этого водопада является первичный ключ. Удаляет поток вниз.

Поэтому, если вы определяете on_delete FK как «CASCADE», вы добавляете запись этого FK в каскад удалений, происходящих из PK. Запись FK может участвовать в этом каскаде или нет («SET_NULL»). На самом деле, запись с FK может даже предотвратить поток удалений! Постройте плотину с помощью «ЗАЩИТА».

Грегори
источник
0

Использование CASCADE означает на самом деле указание Django удалить указанную запись. В приведенном ниже примере приложения для опроса: когда «Вопрос» удаляется, он также удаляет варианты, которые есть у этого Вопроса.

например, вопрос: как вы узнали о нас? (Выбор: 1. Друзья 2. Телевизионная реклама 3. Поисковая система 4. Электронная реклама)

Когда вы удалите этот вопрос, он также удалит все эти четыре варианта из таблицы. Обратите внимание, в каком направлении он течет. Вам не нужно помещать on_delete = models.CASCADE в Question Model, чтобы выбрать его.

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)
Кунал
источник