2010-10-14 21 views
14

Utilizando los siguientes modelos relacionados (una entrada de blog puede tener múltiples revisiones):Django - Cascade supresión en ManyToManyRelation

class BlogEntryRevision(models.Model): 
    revisionNumber = models.IntegerField() 
    title = models.CharField(max_length = 120) 
    text = models.TextField() 
    [...] 

class BlogEntry(models.Model): 
    revisions = models.ManyToManyField(BlogEntryRevision) 
    [...] 

¿Cómo puedo decirle a Django para eliminar todos los relacionados BlogEntryRevision s Cuando se elimina el BlogEntry correspondiente? El valor predeterminado parece ser mantener los objetos en una relación de muchos a muchos si se elimina un objeto del "otro" lado. ¿Hay alguna forma de hacerlo, preferiblemente sin anular BlogEntry.delete?

Respuesta

10

Creo que estás malinterpretando la naturaleza de una relación ManyToMany. Usted habla sobre la eliminación de "BlogEntry correspondiente". Pero el objetivo de ManyToMany es que cada BlogEntryRevision tenga múltiples BlogEntries relacionados. (Y, por supuesto, cada BlogEntry tiene múltiples BlogEntryRevisions, pero ya lo sabe)

A partir de los nombres que ha utilizado, y del hecho de que desea esta funcionalidad de eliminación de cascada, creo que sería mejor con un ForeignKey estándar de BlogEntryRevision a BlogEntry. Siempre que no establezca null=True en esa ForeignKey, las eliminaciones se almacenarán en cassette: cuando se elimine BlogEntry, también se realizarán todas las revisiones.

+2

Sí, admití ese error en el comentario a la otra respuesta. En aras de la exhaustividad: suponiendo que un caso de uso tenga más sentido con 'ManyToManyRelation', ¿cuál sería una buena forma de eliminar en cascada? ¿Funciona el enfoque de la respuesta eliminada de Gabi Purcaru? – AndiDog

+2

Las eliminaciones se realizarán en cascada en '' null = True'' –

+0

El comportamiento de eliminación de cascadas en 'ForeignKey's se puede controlar con [' on_delete'] (https://docs.djangoproject.com/en/dev/ref/models/ fields/# django.db.models.ForeignKey.on_delete) (pero por defecto es 'CASCADE') – meshy

2

Se puede utilizar un custom model manager, pero la documentación parece indicar que does do something like this already y no puedo recordar exactamente lo que esto significa:

El método de eliminación, convenientemente, es llamado delete(). Este método elimina inmediatamente el objeto y tiene sin valor de retorno. Ejemplo:

e.delete() 

También puede eliminar objetos a granel. Cada QuerySet tiene un método delete() , que borra todos los miembros de que QuerySet.

Por ejemplo, este procedimiento elimina toda la entrada objetos con un año pub_date de 2005:

Entry.objects.filter(pub_date__year=2005).delete() 

tener en cuenta que esta voluntad, siempre que sea posible, ser ejecutado puramente en SQL, por lo que el BORRAR () los métodos de instancias de objetos individuales no se llamarán necesariamente a durante el proceso . Si ha proporcionado un método delete() personalizado en una clase de modelo y quiere asegurarse de que se llame, necesitará "manualmente" eliminar instancias de ese modelo (por ejemplo, iterando sobre un QuerySet y llamando al delete() en cada objeto individualmente) en lugar de usar el método bulk delete() de un QuerySet.

Cuando Django borra un objeto, emula el comportamiento del SQL restricción ON DELETE CASCADE - en otras palabras , cualquier objeto que tenían claves externas que apunta al objeto de ser borrado serán eliminados a lo largo con . Por ejemplo:

b = Blog.objects.get(pk=1) 
# This will delete the Blog and all of its Entry objects. 
b.delete() 
+0

derecho, eliminaciones en cascada son el valor predeterminado de 1: N relaciones ('' ForeignKey') como Blog 1: N Entry' en la documentación citada. Pero tengo un 'ManyToManyRelation', por lo que la cascada solo elimina el registro que dice" entrada X y revisión Y pertenecen juntas ", pero no el registro' BlogEntryRevision'. Tal vez debería hacerlo con 'ForeignKey' en mi caso de uso ... De todos modos, considere la pregunta como una pregunta genérica sobre eliminaciones en cascada de muchos a muchos. – AndiDog

8

Tenía este caso de uso exacto de hoy:

  • Modelo Autor: pueden tener varias entradas
  • entrada
  • Modelo: puede tener varios autores

Para esto, estoy usando una ManyToManyRelationship.

Mi caso de uso fue: si elimino la última entrada de un autor en particular, entonces este autor también debe eliminarse.

La solución se puede lograr mediante el pre_delete signal:

@receiver(pre_delete, sender=Entry) 
def pre_delete_story(sender, instance, **kwargs): 
    for author in instance.authors.all(): 
     if author.entries.count() == 1 and instance in author.entries.all(): 
      # instance is the only Entry authored by this Author, so delete it 
      author.delete() 
+0

¿Por qué verificar' y instancia en authors.entries.all() '? Ya estás iterando sobre los autores en la relación de instancia. –

+0

Esa es una buena pregunta, ya no estoy seguro de por qué lo agregué. Probablemente también sea posible sin el control. – jessepeng