Solución:
Este es el comportamiento a adoptar cuando el referenciado se elimina el objeto. No es específico de Django; este es un estándar SQL. Aunque Django tiene su propia implementación sobre SQL. (1)
Hay siete acciones posibles a tomar cuando ocurre tal evento:
-
CASCADE
: Cuando se elimina el objeto al que se hace referencia, también elimine los objetos que tienen referencias a él (cuando elimina una publicación de blog, por ejemplo, es posible que también desee eliminar los comentarios). Equivalente de SQL:CASCADE
. -
PROTECT
: Prohibir la eliminación del objeto referenciado. Para eliminarlo, tendrá que eliminar todos los objetos que hacen referencia a él manualmente. Equivalente de SQL:RESTRICT
. -
RESTRICT
: (introducido en Django 3.1) Comportamiento similar alPROTECT
que coincide con SQLRESTRICT
con más precisión. (Ver ejemplo de documentación de django) -
SET_NULL
: Establezca la referencia en NULL (requiere que el campo sea anulable). Por ejemplo, cuando elimina un usuario, es posible que desee mantener los comentarios que publicó en las publicaciones del blog, pero digamos que fue publicado por un usuario anónimo (o eliminado). Equivalente de SQL:SET NULL
. -
SET_DEFAULT
: Establece el valor predeterminado. Equivalente de SQL:SET DEFAULT
. -
SET(...)
: Establece un valor dado. Este no es parte del estándar SQL y está completamente manejado por Django. -
DO_NOTHING
: Probablemente 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 de 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, CASCADE
es el comportamiento esperado, pero para cada ForeignKey, siempre debe preguntarse cuál es el comportamiento esperado en esta situación. PROTECT
y SET_NULL
a menudo son útiles. Configuración CASCADE
donde no debería, potencialmente puede eliminar toda su base de datos en cascada, simplemente eliminando un solo usuario.
Nota adicional para aclarar la dirección de la cascada
Es gracioso notar que la dirección del CASCADE
La acción no está clara para muchas personas. De hecho, es gracioso notar que solamente los CASCADE
la acción no está clara. Entiendo que el comportamiento en cascada puede ser confuso, sin embargo, debes pensar que es la misma dirección que cualquier otra acción. Por lo tanto, si sientes que CASCADE
la dirección no es clara para ti, en realidad significa que on_delete
el comportamiento no le resulta claro.
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 tienes una entrada comentario_A, que tiene una clave externa para una entrada artículo_B. Si borra la entrada comentario_A, todo esta bien. artículo_B solía vivir sin comentario_A y no te molestes si se borra. Sin embargo, si elimina artículo_B, luego comentario_A pánico! Nunca vivio sin artículo_B y lo necesita, y es parte de sus atributos (article=article_B
, pero que es artículo_B???). Aquí es donde on_delete
pasos en, 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
PROTECT
oRESTRICT
en 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 article_B” y suicidarse (esta es la
CASCADE
comportamiento). -
“Está bien, tengo un amante de repuesto y haré referencia al artículo_C a partir de ahora” (
SET_DEFAULT
, o inclusoSET(...)
). -
“¡No puedo enfrentar la realidad, y 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 al pie
(1) Django tiene su propia implementación sobre SQL. Y, como lo menciona @ JoeMjr2 en los comentarios a continuación, Django no creará las restricciones de SQL. Si desea que las restricciones estén garantizadas por su base de datos (por ejemplo, si su base de datos es utilizada por otra aplicación, o si cuelga 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 a nivel de base de datos sobre restricciones de eliminación en Django.
(2) En realidad, hay un caso en el que
DO_NOTHING
puede ser útil: si desea omitir la implementación de Django e implementar la restricción usted mismo en el nivel de la base de datos.
los on_delete
El método se usa para decirle a Django qué hacer con las instancias de modelo que dependen de la instancia de modelo que elimine. (por ejemplo, un ForeignKey
relación). los on_delete=models.CASCADE
le dice a Django que conecte en cascada el efecto de eliminación, es decir, que también continúe eliminando los modelos dependientes.
He aquí un ejemplo más concreto. Suponga que tiene un Author
modelo que es un ForeignKey
en un Book
modelo. Ahora, si elimina una instancia del Author
modelo, Django no sabría qué hacer con las instancias del Book
modelo que dependen de esa instancia de Author
modelo. los on_delete
El método le dice a Django qué hacer en ese caso. Configuración on_delete=models.CASCADE
le indicará a Django que conecte en cascada el efecto de eliminación, es decir, que elimine todos los Book
instancias modelo que dependen de la Author
instancia de modelo que eliminó.
Nota: on_delete
se convertirá en un argumento obligatorio en Django 2.0. En versiones anteriores, el valor predeterminado es CASCADE
.
Aquí está toda la documentación oficial.
FYI, el on_delete
El parámetro en los modelos está al revés de lo que parece. Pones on_delete
en una clave externa (FK) en un modelo para decirle a Django qué hacer si se elimina la entrada FK a la que está apuntando en su registro. Las opciones que más ha utilizado nuestra tienda son PROTECT
, CASCADE
, y SET_NULL
. Estas son las reglas básicas que he descubierto:
- Usar
PROTECT
cuando su FK apunta a una tabla de búsqueda que realmente no debería cambiar y que ciertamente no debería hacer que su tabla cambie. Si alguien intenta eliminar una entrada en esa tabla de búsqueda,PROTECT
les impide eliminarlo si está vinculado a algún registro. También evita que Django elimine tu registro solo porque eliminó una entrada en una tabla de búsqueda. Esta última parte es fundamental. Si alguien borrara el género “Mujer” de mi tabla Género, CIERTAMENTE NO me gustaría que eso eliminara instantáneamente a todas y cada una de las personas que tenía en mi tabla Persona que tuvieran ese género. - Usar
CASCADE
cuando su FK apunta 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 es borrado, de verdad haría desea que se eliminen todas las entradas de PersonEthnicity “secundarias”. Son irrelevantes sin la Persona. - Usar
SET_NULL
Cuando usted hacer desea que las personas puedan eliminar una entrada en una tabla de búsqueda, pero aún desea 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 búsqueda, yo diríaon_delete=SET_NULL
. Esto dejaría mi registro de persona ahí fuera; simplemente establecería el FK de la escuela secundaria en mi persona en nulo. Obviamente, tendrás que permitirnull=True
en ese 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 especificar on_delete
(o no lo hizo), 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, establezca on_delete=models.PROTECT
. “Luego, pruebe su aplicación. Descubrirá rápidamente qué FK deben etiquetarse con los otros valores sin poner en peligro ninguno de sus datos.
Además, vale la pena señalar que on_delete=CASCADE
en 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 predeterminado, así que poner on_delete=CASCADE
es lo mismo que poner nada.