¿Qué hace on_delete en los modelos Django?

Resuelto All Іѕ Vаиітy asked hace 8 años • 11 respuestas

Estoy bastante familiarizado con Django, pero recientemente noté que existe una on_delete=models.CASCADEopción con los modelos. He buscado la documentación sobre el mismo, pero no pude encontrar nada más que:

Modificado en Django 1.9:

on_deleteahora se puede utilizar como segundo argumento posicional (anteriormente, normalmente solo se pasaba como argumento de palabra clave). Será un argumento obligatorio en Django 2.0.

Un caso de ejemplo de uso es :

from django.db import models

class Car(models.Model):
    manufacturer = models.ForeignKey(
        'Manufacturer',
        on_delete=models.CASCADE,
    )
    # ...

class Manufacturer(models.Model):
    # ...
    pass

¿Qué hace on_delete? ( Supongo que las acciones a realizar si se elimina el modelo ).

¿Que es lo que models.CASCADEhace? ( cualquier sugerencia en la documentación )

¿Qué otras opciones están disponibles ( si mi suposición es correcta? )?

¿Dónde reside la documentación para esto?

All Іѕ Vаиітy avatar Jul 15 '16 12:07 All Іѕ Vаиітy
Aceptado

Este es el comportamiento a adoptar cuando se elimina el objeto referenciado . No es específico de Django; este es un estándar SQL. Aunque Django tiene su propia implementación además de SQL. (1)

Hay siete acciones posibles a tomar cuando ocurre tal evento:

  • CASCADE: Cuando se elimina el objeto al que se hace referencia, elimine también los objetos que tengan referencias a él (cuando elimina una publicación de blog, por ejemplo, es posible que desee eliminar también los comentarios). Equivalente SQL: CASCADE.
  • PROTECT: Prohibir la eliminación del objeto referenciado. Para eliminarlo tendrás que eliminar todos los objetos que hacen referencia a él manualmente. Equivalente SQL: RESTRICT.
  • RESTRICT: (introducido en Django 3.1) Comportamiento similar al PROTECTque coincide con SQL RESTRICTcon mayor precisión. (Ver ejemplo de documentación de Django )
  • SET_NULL: establece la referencia en NULL (requiere que el campo admita valores NULL). Por ejemplo, cuando elimina un usuario, es posible que desee conservar los comentarios que publicó en las publicaciones del blog, pero decir que fueron publicados por un usuario anónimo (o eliminado). Equivalente SQL: SET NULL.
  • SET_DEFAULT: establece el valor predeterminado. Equivalente SQL: SET DEFAULT.
  • SET(...): establece un valor determinado. Éste no forma parte del estándar SQL y lo maneja enteramente Django.
  • DO_NOTHING: Probablemente sea una muy mala idea ya que esto crearía problemas de integridad en su base de datos (haciendo referencia a un objeto que en realidad no existe). Equivalente SQL: NO ACTION. (2)

Fuente: documentación de Django

Consulte también la documentación de PostgreSQL, por ejemplo.

En la mayoría de los casos, CASCADEes el comportamiento esperado, pero para cada ForeignKey, siempre debes preguntarte cuál es el comportamiento esperado en esta situación. PROTECTy SET_NULLsuelen ser útiles. Configurarlo CASCADEdonde no debería hacerlo puede potencialmente eliminar toda su base de datos en cascada, simplemente eliminando un solo usuario.


Nota adicional para aclarar la dirección de la cascada

Es curioso notar que la dirección de la CASCADEacción no está clara para muchas personas. En realidad, es curioso notar que sólo la CASCADEacción no está clara. Entiendo que el comportamiento en cascada puede resultar confuso, sin embargo debes pensar que es la misma dirección que cualquier otra acción . Por lo tanto, si siente que CASCADEla dirección no está clara para usted, en realidad significa que on_deleteel comportamiento no está claro para usted.

En su base de datos, una clave externa está básicamente representada por un campo entero cuyo valor es la clave principal del objeto externo. Digamos que tiene una entrada comment_A , que tiene una clave externa para una entrada Article_B . Si eliminas la entrada comment_A , todo está bien. El artículo_B solía vivir sin el comentario_A y no se molesta si se elimina. Sin embargo, si eliminas el artículo_B , entonces el comentario_A entra en pánico. Nunca vivió sin artículo_B y lo necesita, es parte de sus atributos ( article=article_B, pero ¿qué es artículo_B ???). Aquí es donde on_deleteinterviene para determinar cómo resolver este error de integridad , ya sea diciendo:

  • "¡No! ¡Por favor! ¡No lo hagas! ¡No puedo vivir sin ti!" (que se dice PROTECTo RESTRICTen Django/SQL)
  • "Está bien, si no soy tuyo, entonces no soy de nadie" (que se dice SET_NULL)
  • "Adiós mundo, no puedo vivir sin el artículo_B" y suicidarme (este es el CASCADEcomportamiento).
  • "Está bien, tengo un amante de repuesto, haré referencia al artículo_C a partir de ahora" ( SET_DEFAULT, o incluso SET(...)).
  • "No puedo afrontar la realidad, ¡seguiré gritando tu nombre incluso si eso es lo único que me queda!" ( DO_NOTHING)

Espero que aclare la dirección de la cascada. :)


Notas a pie de página

(1) Django tiene su propia implementación además de SQL. Y, como lo menciona @JoeMjr2 en los comentarios a continuación , Django no creará las restricciones SQL. Si desea que su base de datos garantice las restricciones (por ejemplo, si su base de datos es utilizada por otra aplicación, o si se cuelga en la consola de la base de datos de vez en cuando), es posible que desee establecer las restricciones relacionadas manualmente usted mismo. Hay un ticket abierto para agregar soporte para restricciones de eliminación a nivel de base de datos en Django.

(2) En realidad, hay un caso en el que DO_NOTHINGpuede resultar útil: si desea omitir la implementación de Django e implementar la restricción usted mismo a nivel de base de datos.

Antoine Pinsard avatar Jul 15 '2016 06:07 Antoine Pinsard

El on_deletemétodo se utiliza para decirle a Django qué hacer con las instancias del modelo que dependen de la instancia del modelo que elimine. (por ejemplo, una ForeignKeyrelación). Le on_delete=models.CASCADEdice a Django que ponga en cascada el efecto de eliminación, es decir, que continúe eliminando también los modelos dependientes.

He aquí un ejemplo más concreto. Suponga que tiene un Authormodelo que está ForeignKeyen un Bookmodelo. Ahora, si eliminas una instancia del Authormodelo, Django no sabrá qué hacer con las instancias del Bookmodelo que dependen de esa instancia del Authormodelo. El on_deletemétodo le dice a Django qué hacer en ese caso. La configuración on_delete=models.CASCADEle indicará a Django que ponga en cascada el efecto de eliminación, es decir, elimine todas las Bookinstancias del modelo que dependen de la Authorinstancia del modelo que eliminó.

Nota: on_deletese convertirá en un argumento obligatorio en Django 2.0. En versiones anteriores el valor predeterminado es CASCADE.

Aquí está toda la documentación oficial.

him229 avatar Jul 15 '2016 06:07 him229

Para su información, el on_deleteparámetro en los modelos está al revés de lo que parece. Colocas on_deleteuna clave externa (FK) en un modelo para decirle a Django qué hacer si se elimina la entrada FK a la que estás apuntando en tu registro. Las opciones que más ha utilizado nuestra tienda son PROTECT, CASCADEy SET_NULL. Estas son las reglas básicas que he descubierto:

  1. Úselo PROTECTcuando su FK apunte a una tabla de búsqueda que realmente no debería cambiar y que ciertamente no debería causar que su tabla cambie. Si alguien intenta eliminar una entrada en esa tabla de búsqueda, PROTECTle impide eliminarla si está vinculada a algún registro. También evita que Django borre su registro sólo porque eliminó una entrada en una tabla de búsqueda. Esta última parte es crítica. Si alguien eliminara el género "Mujer" de mi tabla Género, CIERTAMENTE NO quisiera que eso eliminara instantáneamente a todas y cada una de las personas que tenía en mi tabla Persona que tenían ese género.
  2. Úselo CASCADEcuando su FK apunte a un registro "principal". Por lo tanto, si una persona puede tener muchas entradas de PersonEthnicity (puede ser indio americano, negro y blanco) y esa persona se elimina, realmente me gustaría que se eliminen todas las entradas de PersonEthnicity "secundarias". Son irrelevantes sin la Persona.
  3. Úselo SET_NULLcuando desee que las personas puedan eliminar una entrada en una tabla de búsqueda, pero aún desee conservar su registro. Por ejemplo, si una persona puede tener una escuela secundaria, pero realmente no me importa si esa escuela secundaria desaparece en mi tabla de consulta, diría on_delete=SET_NULL. Esto dejaría mi registro de Persona ahí fuera; simplemente establecería el FK de la escuela secundaria en mi Persona como nulo. Obviamente, tendrás que permitir null=Trueese FK.

A continuación se muestra un ejemplo de un modelo que hace las tres cosas:

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'

Como último dato, ¿sabías que si no especificas on_delete(o no lo hiciste), el comportamiento predeterminado es CASCADE? Esto significa que si alguien eliminó una entrada de género en su tabla Género, ¡también se eliminaron todos los registros de Persona con ese género!

Yo diría: "En caso de duda, configure on_delete=models.PROTECT". Luego prueba tu aplicación. Descubrirá rápidamente qué FK deben etiquetarse como otros valores sin poner en peligro ninguno de sus datos.

Además, vale la pena señalar que on_delete=CASCADEen realidad no se agrega a ninguna de sus migraciones, si ese es el comportamiento que está seleccionando. Supongo que esto se debe a que es el valor predeterminado, por lo que poner on_delete=CASCADEes lo mismo que no poner nada.

HelenM avatar Dec 21 '2017 19:12 HelenM

Como se mencionó anteriormente, CASCADE eliminará el registro que tiene una clave externa y hace referencia a otro objeto que fue eliminado. Entonces, por ejemplo, si tiene un sitio web de bienes raíces y tiene una propiedad que hace referencia a una ciudad

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

y ahora, cuando la ciudad se elimine de la base de datos, todas las propiedades asociadas (por ejemplo, bienes raíces ubicadas en esa ciudad) también se eliminarán de la base de datos.

Ahora también quiero mencionar el mérito de otras opciones, como SET_NULL o SET_DEFAULT o incluso DO_NOTHING. Básicamente, desde la perspectiva de la administración, desea "eliminar" esos registros. Pero realmente no quieres que desaparezcan. Por muchas razones. Es posible que alguien lo haya eliminado accidentalmente o con fines de auditoría y seguimiento. Y reportajes sencillos. Por lo que puede ser una manera de “desconectar” la propiedad de una Ciudad. Nuevamente, dependerá de cómo esté escrita su solicitud.

Por ejemplo, algunas aplicaciones tienen un campo "eliminado" que es 0 o 1. Y todas sus búsquedas y vistas de lista, etc., cualquier cosa que pueda aparecer en los informes o en cualquier lugar al que el usuario pueda acceder desde la interfaz, excluye todo lo que sea deleted == 1. Sin embargo, si crea un informe personalizado o una consulta personalizada para desplegar una lista de registros que se eliminaron y aún más para ver cuándo se modificó por última vez (otro campo) y por quién (es decir, quién lo eliminó y cuándo). eso es muy ventajoso desde el punto de vista ejecutivo.

Y no olvide que puede revertir eliminaciones accidentales de forma tan sencilla como deleted = 0la de esos registros.

Lo que quiero decir es que si hay una funcionalidad, siempre hay una razón detrás de ella. No siempre es una buena razón. Pero una razón. Y muchas veces también una buena.

George Mogilevsky avatar Jul 19 '2018 15:07 George Mogilevsky

Usar CASCADE significa en realidad decirle a Django que elimine el registro al que se hace referencia. En el siguiente ejemplo de la aplicación de encuesta: Cuando se elimina una 'Pregunta', también se eliminarán las opciones que tiene esa pregunta.

Por ejemplo, Pregunta: ¿Cómo se enteró de nosotros? (Opciones: 1. Amigos 2. Anuncio de televisión 3. Motor de búsqueda 4. Promoción por correo electrónico)

Cuando elimine esta pregunta, también eliminará estas cuatro opciones de la tabla. Tenga en cuenta en qué dirección fluye. No tienes que poner on_delete=models.CASCADE en el Modelo de pregunta, ponlo en la Elección.

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)
Kunal Kumar avatar Jan 25 '2020 07:01 Kunal Kumar