Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

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

Я надеюсь, что это делает каскадное направление более ясным. :)

Ответ 2

Метод 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.

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

Ответ 3

К сведению, параметр on_delete в моделях 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 когда вы хотите, чтобы людям было разрешено удалить запись в SET_NULL таблице, но вы все равно хотите сохранить свою запись. Например, если у человека может быть старшая школа, но для меня не имеет значения, исчезнет ли эта старшая школа из моей on_delete=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 (или не on_delete), то по умолчанию будет CASCADE? Это означает, что если кто-то удалил запись о полах в вашей таблице "Гендер", все записи о персонах с этим полом также будут удалены!

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

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

django==1.11.22 имеет следующий код. Это означает, что on_delete умолчанию имеет значение CASCADE.

class ForeignKey(ForeignObject):
    .......
        if on_delete is None:
            warnings.warn(
                "on_delete will be a required arg for %s in Django 2.0. Set "
                "it to models.CASCADE on models and in existing migrations "
                "if you want to maintain the current default behavior. "
                "See https://docs.djangoproject.com/en/%s/ref/models/fields/"
                "#django.db.models.ForeignKey.on_delete" % (
                    self.__class__.__name__,
                    get_docs_version(),
                ),
                RemovedInDjango20Warning, 2)
            on_delete = CASCADE
    .......