що робить 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

Відповіді:


784

Це поведінка, яку слід прийняти при видаленні посилального об'єкта. Це не специфічно для 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.

Джерело: Документація Джанго

Дивіться також документацію PostGreSQL , наприклад.

У більшості випадків CASCADEце очікувана поведінка, але для кожного ForeignKey завжди слід запитати себе, якою є очікувана поведінка в цій ситуації. PROTECTі SET_NULLчасто корисні. Встановивши, CASCADEде він не повинен, потенційно можна видалити всю базу даних у каскаді, просто видаливши одного користувача.


Додаткова примітка для уточнення напрямку каскаду

Смішно помітити, що напрямок CASCADEдії багатьом не зрозумілий. На насправді, це забавно відзначити , що тількиCASCADE дія не ясно. Я розумію, що поведінка каскаду може бути заплутаною, однак ви повинні думати, що це той самий напрямок, як і будь-яка інша дія . Таким чином, якщо ви відчуваєте, що CASCADEнапрямок вам не зрозумілий, це насправді означає, що on_deleteповедінка вам не зрозуміла.

У вашій базі даних зовнішній ключ в основному представлений цілим полем, значення якого є первинним ключем іноземного об'єкта. Скажімо, у вас є коментар до запису_А , у якому є зовнішній ключ до статті_B . Якщо видалити запис comment_A , все нормально, article звикли жити без comment_A і не турбуйтеся , якщо він віддалений. Однак якщо ви видалите article_B , тоді коментар_А панікуйте ! Він ніколи не жив без 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, хто має поле ФК у своїй таблиці, тоді як 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.

Ось вся офіційна документація.


37

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

  1. Використовуйте, PROTECTколи ваш FK вказує на оглядову таблицю, яка насправді не повинна змінюватися і яка, безумовно, не повинна спричиняти зміни вашої таблиці. Якщо хтось намагається видалити запис із цієї таблиці пошуку, PROTECTне дозволяє їм видалити його, якщо він прив’язаний до будь-яких записів. Він також не дозволяє django видалити ваш запис лише тому, що він видалив запис із таблиці пошуку. Ця остання частина є критичною. Якби хтось видалив гендерну групу "Жінки" з моєї гендерної таблиці, Я НАРОДНО НЕ хотів би, щоб миттєво видаляв усіх людей, які були в моїй таблиці Особи, які мали цю стать.
  2. Використовуйте, CASCADEколи ваш FK вказує на запис "батьків". Отже, якщо у Особи може бути багато записів PersonEthnicity (він / вона може бути американським індіанцем, чорним і білим), і ця особа буде видалена, я дуже хотів би видалити будь-які "дитячі" записи PersonEthnicity. Вони не мають значення без Особи.
  3. Використовуйте , SET_NULLякщо ви дійсно хочете , щоб люди , щоб мати можливість видалити запис на довідкову таблицю, але ви все ще хочете зберегти запис. Наприклад, якщо людина може мати середню школу, але це не дуже важливо для мене, якщо ця гімназія піде на мій оглядовий стіл, я б сказав on_delete=SET_NULL. Це залишило б мою особу запису там; це просто встановить нульову школу ФК на моїй Особі на нуль. Очевидно, вам доведеться дозволити 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? Це означає, що якщо хтось видалив гендерний запис із вашої гендерної таблиці, будь-які записи персоналу з цією гендерною категорією також були видалені!

Я б сказав: "Якщо ви сумніваєтесь, поставте on_delete=models.PROTECT". Потім перейдіть на тестування своєї заявки. Ви швидко зрозумієте, на яких ФК слід позначати інші значення, не загрожуючи жодним вашим даним.

Також варто зауважити, що 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
Це було корисно, оскільки воно уточнювало, в якому напрямку відбувається КАСКАД. Прийнята відповідь не зрозуміла, якщо ви незнайомі з каскадами SQL.
codecribblr

Дякую :) дуже вдячний!
Георгій Могилевський

2
Я підтримую цю відповідь, тому що вона відповідає моїм сумнівам щодо напрямку у моделі стосунків
edepe

6

Ось відповідь на ваше запитання, що говорить: чому ми використовуємо 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: каскад видаляє; за замовчуванням

ЗАХИСТ: Запобігайте видаленню посиланого об'єкта, піднявши ProtectedError, підклас django.db.IntegrityError.

SET_NULL: встановлення нуля ForeignKey; це можливо лише в тому випадку, якщо нульовим є значення True.

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


Прості слова дають зрозуміти мені, оскільки я ще не дозріла з sql та django. Дякую.
wm.p1us

3

Скажімо, у вас є дві моделі, одну з іменем Person та іншу на ім'я Company .

За визначенням, одна людина може створити більше однієї компанії.

Враховуючи, що компанія може мати одну і лише одну особу, ми хочемо, щоб при видаленні особи були видалені також усі компанії, пов’язані з цією особою.

Отже, ми почнемо із створення моделі 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

Переорієнтуйте свою ментальну модель на функціональність "CASCADE", подумавши про додавання FK до вже існуючого каскаду (тобто водоспаду). Джерело цього водоспаду - Первинний ключ. Видаляє стік вниз.

Отже, якщо ви визначаєте on_delete FK як "CASCADE", ви додаєте цей запис FK у каскад делетів, що походять з ПК. Запис 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.