2012-09-29 13 views
10

Django muestra cómo establecer o anular las eliminaciones en cascada con claves externas en sus documentos.Django Cascade Delete en las teclas externas inversas

model = models.ForeignKey(MyModel, null = True, on_delete = models.SET_NULL) 

Pero, ¿y si quisiéramos este efecto al revés? ¿Qué pasa si queremos que la eliminación del modelo fk resulte en la eliminación de este modelo?

Gracias

Respuesta

0

No creo que la función que está viendo es un concepto ORM o base de datos. Solo quiere ejecutar una devolución de llamada cuando se elimina algo.

a fin de utilizar el post_deletesignal y se agrega manejador de devolución de llamada no

from django.db.models.signals import post_delete 
from django.dispatch import receiver 
from myapp.models import MyModel 

@receiver(post_delete, sender=MyModel) 
def my_post_delete_callback(sender, **kwargs): 
    #Sender is the model which when deleted should trigger this action 
    #Do stuff like delete other things you want to delete 
    #The object just deleted can be accessed as kwargs[instance] 
+0

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

8

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.

+0

Por cierto, esto probablemente no causaría una recursión infinita, ¿verdad? – minmaxavg

Cuestiones relacionadas