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

157

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

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

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

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

5 ответов

270
Лучший ответ

Это поведение, которое нужно применять при удалении ссылочного объекта. Это не специфично для 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 там, где это не должно, потенциально может удалить всю вашу базу данных в каскаде, просто удалив одного пользователя.

  • 9
    Глупый вопрос, но каскад всегда должен быть однонаправленным, верно? Т.е. если у Comment есть внешний ключ к BlogPost то удаление BlogPost должно удалить комментарий, но удаление Comment не должно удалять BlogPost, независимо от RDMS?
  • 14
    @AnthonyManningFranklin Конечно. При удалении активируется только тогда, когда ссылка «не работает». Это не тот случай, когда вы удаляете комментарий, так как вы удаляете ссылку одновременно.
Показать ещё 2 комментария
29

Метод on_delete используется для указания Django, что делать с экземплярами модели, которые зависят от экземпляра модели, который вы удаляете. (например, a 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.

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

18

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

  • Используйте PROTECT, когда ваш FK указывает на справочную таблицу, которая действительно не должна меняться и которая, безусловно, не должна приводить к изменению вашей таблицы. Если кто-то пытается удалить запись в этой справочной таблице, PROTECT запрещает их удалять, если она привязана к любой записи. Это также предотвращает удаление django вашей записи только потому, что она удалила запись в справочной таблице. Последняя часть важна. Если бы кто-то удалил пол "Женщина" из моей таблицы "Пол", я НЕ НЕОБХОДИМО, чтобы немедленно удалить всех и всех людей, которых я имел в таблице Person, которые имели этот пол.
  • Используйте CASCADE, когда ваш FK указывает на "родительскую" запись. Итак, если у Человека может быть много записей PersonEnnicity (он может быть американским индейцем, черным и белым), и этот Человек удаляется, мне действительно хотелось бы, чтобы все записи "Person" PersonEthnicity были удалены. Они не имеют значения без Человека.
  • Используйте SET_NULL, когда вы хотите, чтобы людям разрешалось удалять запись в справочной таблице, но вы все равно хотите сохранить свою запись. Например, если у Человека может быть HighSchool, но для меня это не имеет значения, если эта средняя школа уйдет в мою справочную таблицу, я бы сказал "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? Это означает, что если кто-то удалил гендерную запись в вашей таблице Gender, все записи Person с этим полом также были удалены!

Я бы сказал: "Если есть сомнения, установите on_delete = models.PROTECT." Затем протестируйте свое приложение. Вы быстро выясните, какие FK следует пометить другими значениями, не подвергая опасности какие-либо данные.

Кроме того, стоит отметить, что on_delete = CASCADE фактически не добавляется ни к одной из ваших миграций, если это поведение, которое вы выбираете. Я думаю, это потому, что это значение по умолчанию, поэтому put on_delete = CASCADE - это то же самое, что ничего не делать.

1

Вот ответ на ваш вопрос, который говорит: почему мы используем 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 должно быть установлено.

1

Как упоминалось ранее, 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 для этих записей.

Я хочу сказать, что если есть функциональность, то всегда есть причина. Не всегда веская причина. Но причина. И часто хороший тоже.

  • 0
    Это было полезно, потому что выяснило, в каком направлении происходит КАСКАД. Принятый ответ не ясен, если вы не знакомы с каскадами SQL.
  • 0
    Спасибо :) очень ценится!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню