Hay un punto de aplicación muy delicada, que pensé que debería añadir a esta discusión.
Digamos que tenemos dos modelos, uno de los cuales hace referencia a la otra por una clave externa, como en:
class A(models.Model):
x = models.IntegerField()
class B(models.Model):
a = models.ForeignKey(A, null=True, blank=True)
Ahora bien, si elimina una entrada de A, el comportamiento en cascada hará que la referencia en B para ser eliminado también.
Hasta ahora, todo bien. Ahora queremos revertir este comportamiento. La manera obvia como la gente ha mencionado es usar las señales emitidas durante la eliminación, entonces vamos:
def delete_reverse(sender, **kwargs):
if kwargs['instance'].a:
kwargs['instance'].a.delete()
post_delete.connect(delete_reverse, sender=B)
Esto parece ser perfecto. ¡Incluso funciona! Si eliminamos una entrada B, también se eliminará la A correspondiente.
El PROBLEMA es que tiene un comportamiento circular que causa una excepción: si eliminamos un elemento de A, debido al comportamiento de cascada predeterminado (que queremos mantener), también se eliminará el elemento correspondiente de B, lo que hará que se llame a delete_reverse, ¡que intenta eliminar un elemento ya eliminado!
El truco es, es necesario el manejo de excepciones para la correcta aplicación de la cascada inversa:
def delete_reverse(sender, **kwargs):
try:
if kwargs['instance'].a:
kwargs['instance'].a.delete()
except:
pass
Este código funcionará en ambos sentidos. Espero que ayude a algunas personas.
Esta estrategia no es confiable porque la señal 'on_delete' es emitida por el método' delete() 'y el método' delete() 'no garantiza que se llame] (https://docs.djangoproject.com/es/dev/topics/db/queries/# deleting-objects), "siempre que sea posible, esto se ejecutará puramente en SQL, por lo que los métodos delete() de las instancias de objetos individuales no se llamarán necesariamente durante el proceso. Si ha proporcionado un método delete() personalizado en una clase de modelo ... " – claytond