2010-10-14 2 views
14

Mit den folgenden verwandten Modelle (ein Blog-Eintrag können mehrere Versionen haben):Django - Cascade Deletion in 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) 
    [...] 

Wie kann ich Django sagen alle zugehörigen BlogEntryRevision s zu löschen, wenn der entsprechende BlogEntry gelöscht wird? Der Standardwert scheint Objekte in einer Viele-zu-Viele-Beziehung zu halten, wenn ein Objekt der "anderen" Seite gelöscht wird. Irgendeine Möglichkeit dies zu tun - vorzugsweise ohne zu überschreiben BlogEntry.delete?

Antwort

10

Ich glaube, Sie die Natur einer ManyToMany Beziehung sind Missverständnis. Sie sprechen davon, dass "der entsprechende Blogeintrag" gelöscht wird. Aber der ganze Punkt einer ManyToMany ist, dass jeder BlogEntryRevision hat mehrere BlogEntries im Zusammenhang damit. (Und natürlich hat jeder BlogEntry mehrere BlogEntryRevisions, aber das wissen Sie bereits.)

Von den Namen, die Sie verwendet haben, und der Tatsache, dass Sie diese Deletionskaskade-Funktion möchten, denke ich, dass Sie mit einem besser dran wären Standard ForeignKey von BlogEntryRevision zu BlogEntry. Solange Sie null=True nicht auf diesem ForeignKey setzen, werden Löschungen cascase - wenn der BlogEntry gelöscht wird, werden auch alle Revisionen.

+2

Ja, ich habe diesen Fehler im Kommentar zu der anderen Antwort zugegeben. Der Vollständigkeit halber: Unter der Annahme eines Anwendungsfalles, der mit 'ManyToManyRelation' sinnvoller ist, was wäre ein guter Weg, um Löschungen zu kaskadieren? Gilt der Ansatz aus der gelöschten Antwort von Gabi Purcaru? – AndiDog

+2

Löschvorgänge kaskadieren auf '' null = True'' –

+0

Kaskadenlöschverhalten auf 'ForeignKey's kann mit [' on_delete'] gesteuert werden (https://docs.djangoproject.com/de/dev/ref/models/ fields/# django.db.models.ForeignKey.on_delete) (aber standardmäßig auf 'CASCADE') – meshy

2

Sie ein custom model manager verwenden können, aber die Dokumentation scheint darauf hinzudeuten, dass es does do something like this already und ich nicht genau erinnern kann, was das bedeutet:

die Delete-Methode, bequem, ist löschen genannt(). Diese Methode löscht das Objekt sofort und hat keinen Rückgabewert. Beispiel:

e.delete() 

Sie auch Objekte in loser Schüttung löschen. Jedes QuerySet hat eine delete() Methode, die alle Mitglieder von dieses QuerySet löscht.

Zum Beispiel löscht alle Eintrag Objekte mit einem pub_date Jahr 2005:

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

Beachten Sie, dass dieser Wille, wann immer möglich, rein in SQL ausgeführt werden, und so den Lösch () Methoden der einzelnen Objektinstanzen werden während des Prozesses nicht unbedingt aufgerufen. Wenn Sie eine benutzerdefinierte zur Verfügung gestellt haben löschen() -Methode auf einer Modellklasse und sicherstellen wollen, dass es genannt wird, müssen Sie „manuell“ Instanzen dieses Modell löschen (zB durch über einen QuerySet Iterieren und Aufruf delete() auf jedes Objekt einzeln) anstatt die Massen-delete() Methode eines QuerySet.

Wenn Django ein Objekt löscht, es das Verhalten der SQL Einschränkung ON DELETE CASCADE emuliert - in anderen Worten, alle Objekte, die auf dem Objekt zu zeigen Fremdschlüssel hatte zusammen werden gelöscht gelöscht werden mit es. Zum Beispiel:

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

Recht, Cascading Löschungen sind der Standard für 1: N ('ForeignKey') Beziehungen wie' Blog 1: in der zitierten Dokumentation N Entry'

Die Lösung kann mit dem pre_delete signal erreicht werden. Aber ich habe eine 'ManyToManyRelation', also löscht die Kaskade nur den Datensatz, der besagt, dass" Eintrag X und Revision Y zusammengehören ", aber nicht den' BlogEntryRevision' Datensatz selbst. Vielleicht sollte ich es mit 'ForeignKey' in meinem Anwendungsfall tun ... Wie auch immer, betrachte die Frage als generische Frage bei vielen-zu-vielen kaskadierenden Löschungen. – AndiDog

8

hatte ich genau diesen Anwendungsfall Heute:

  • Modell Autor: mehrere Einträge
  • Modell Eintrag haben kann: kann für diesen

mehrere Autoren haben, ich bin mit einem ManyToManyRelationship.

Mein Anwendungsfall war: Wenn ich den letzten Eintrag eines bestimmten Autors lösche, sollte dieser Autor ebenfalls gelöscht werden.

@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

Warum check' und instance in authors.entries.all() '? Sie durchlaufen bereits Autoren in der Instanzbeziehung. –

+0

Das ist eine gute Frage, ich bin mir nicht mehr sicher, warum ich es hinzugefügt habe. Es ist wahrscheinlich auch ohne den Scheck möglich. – jessepeng