Django - Eliminar en cascada en la relación de muchos a muchos
-
30-09-2019 - |
Pregunta
El uso de los siguientes modelos relacionados (una entrada de blog puede tener múltiples revisiones):
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 BlogEntryRevision
s relacionados cuando se elimina el BlogEntry
correspondiente? El valor por defecto parece ser la de mantener los objetos en una relación de muchos a muchos, si se elimina un objeto del "otro" lado. Cualquier manera de hacer esto? - preferiblemente sin anulando BlogEntry.delete
Solución
creo que está malinterpretando la naturaleza de una relación ManyToMany. Se habla de "la entrada de blog correspondiente" se va a eliminar. Pero el punto central de un ManyToMany es que cada BlogEntryRevision tiene múltiples BlogEntries relacionados con ella. (Y, por supuesto, cada uno tiene múltiples BlogEntry BlogEntryRevisions, pero ya se sabe que ya.)
A partir de los nombres que ha usado, y el hecho de que desea que esta funcionalidad eliminación en cascada, creo que sería mejor con un estándar de ForeignKey BlogEntryRevision a BlogEntry. Mientras no se establece null=True
en ese ForeignKey, supresiones en cascada -. Cuando se elimina la entrada de blog, todas las revisiones serán demasiado
A partir de Django 2.0
El ForeignKey
inicializador ahora requiere que se especifique el parámetro on_delete
:
from django.db import models
from .models import MyRelatedModel
class model(models.Model):
related_model = models.ForeignKey(MyRelatedModel, on_delete=models.CASCADE)
Otros consejos
Tenía este caso de uso exacto de hoy:
- Modelo Autor: puede tener varias entradas
- Modelo Fecha de entrada: puede tener varios autores
Para esto, estoy usando un ManyToManyRelationship.
Mi caso de uso fue: si elimino la última entrada de un autor en particular, entonces este autor debe suprimirse también.
La solución se puede lograr utilizando la pre_delete
señal :
@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()
Se puede utilizar un modelo personalizado gerente , pero la documentación parece indicar que hace algo como esto ya 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 valor de retorno. Ejemplo:
e.delete()
También puede eliminar objetos al por mayor. Cada QuerySet tiene un delete () método, que elimina todos los miembros de que QuerySet.
Por ejemplo, esto elimina todas las entradas los objetos con un año pub_date de 2005:
Entry.objects.filter(pub_date__year=2005).delete()
Tenga en cuenta que esta voluntad, siempre que sea posible, ser ejecutado puramente en SQL, y por lo que el delete () de instancias de objetos individuales no se necesariamente ser llamado durante el proceso. Si usted ha proporcionado una costumbre eliminar () método en una clase de modelo y querrá asegurarse de que se le llama, se tendrá que "manualmente" eliminar instancias de dicho modelo (por ejemplo, por iterar sobre un QuerySet y llamadas delete () en cada objeto individual) en lugar de utilizar la mayor delete () método de un QuerySet.
Cuando Django borra un objeto, emula el comportamiento del SQL restricción ON DELETE CASCADE - en Es decir, cualquier objeto que tenían claves externas apuntando hacia el objeto a suprimirse serán borrados junto con . It Por ejemplo:
b = Blog.objects.get(pk=1) # This will delete the Blog and all of its Entry objects. b.delete()
Simplemente utilice el clear()
método para eliminar los objetos relacionados desde Django usa el pase modelo para especificar las relaciones que el método claro elimina todos los BlogEntryRevision
relacionado
be = BlogEntry.objects.get(id=1)
be.blogentryrevision_set.clear()