2010-10-14 12 views
14

stosując następujące modele pokrewnych (jeden wpis w blogu może mieć wiele poprawek):Django - usunięcie Kaskada w 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) 
    [...] 

Jak mogę powiedzieć Django, aby usunąć wszystkie związane BlogEntryRevision S Kiedy odpowiadająca BlogEntry zostanie usunięty? Domyślnie wydaje się, że zachowujemy obiekty w relacji wiele do wielu, jeśli obiekt "drugiej" strony zostanie usunięty. Jakikolwiek sposób to zrobić - najlepiej bez przesłonięcia BlogEntry.delete?

Odpowiedz

10

myślę, że są niezrozumienia charakteru relacji ManyToMany. Mówisz o tym, że "odpowiedni wpis BlogEntry" został usunięty. Ale cały aspekt ManyToMany polega na tym, że każdy BlogEntryRevision ma wiele powiązanych z nim powiązań. (I, oczywiście, każdy BlogEntry ma wiele BlogEntryRevisions, ale już to wiesz.)

Od nazw używanych przez Ciebie i faktu, że chcesz tę funkcję kaskady usuwania, myślę, że lepiej byłoby, gdyby standardowy ForeignKey z BlogEntryRevision na BlogEntry. Dopóki nie ustawisz null=True na tym kluczu obcym, skasowania zostaną skasowane - po usunięciu elementu BlogEntry wszystkie wersje również będą.

+2

Tak, przyznałem, że błąd w komentarzu do drugiej odpowiedzi. W celu zapewnienia kompletności: Zakładając, że przypadek użycia ma sens z "ManyToManyRelation", jaki byłby dobry sposób na kaskadowe usuwanie? Czy podejście z usuniętej odpowiedzi Gabi Purcaru działa? – AndiDog

+2

Usunięcia będą kaskadowane na '' null = True'' –

+0

Zachowanie Cascade-delete na 'ForeignKey's może być kontrolowane przez [' on_delete'] (https://docs.djangoproject.com/en/dev/ref/models/ fields/# django.db.models.ForeignKey.on_delete) (ale domyślnie 'CASCADE') – meshy

2

Można użyć custom model manager, ale dokumentacja wydaje się wskazywać, że does do something like this already i nie mogę sobie przypomnieć dokładnie, co to oznacza:

metodę Delete, wygodnie jest nazwie kasowania(). Ta metoda powoduje natychmiastowe usunięcie obiektu i brak wartości zwracanej. Przykład:

e.delete() 

Można również zbiorczo usuwać obiekty . Każdy QuerySet ma metodę delete() , która usuwa wszystkich członków z QuerySet.

Na przykład ten usuwa wszystkie wejścia obiekty z roku pub_date 2005:

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

pamiętać, że ta wola, gdy możliwe, być wykonywane wyłącznie w SQL, a więc usuwanie () instancje poszczególnych obiektów nie będą koniecznie wywoływane podczas procesu . Jeśli podałeś niestandardową metodę delete() na klasie modelu i , chcesz się upewnić, że jest ona wywoływana, musisz "ręcznie" usunąć instancje tego modelu (np. iterując po QuerySet i wywoływanie delete() na każdym obiekcie pojedynczo) zamiast używania metody bulk delete() metody QuerySet.

Kiedy Django usuwa obiekt, to emuluje zachowanie SQL ograniczeniem DELETE CASCADE - w Innymi słowy, wszelkie obiekty, które miały kluczy obcych, wskazując na obiekt do zostać usunięte zostaną usunięte wraz z go. Na przykład:

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

prawo, usuwanie kaskadowe są domyślne dla 1: N ('ForeignKey') relacje jak' Blog 1: N WPŁYNIĘCIE w cytowanym dokumentacji. Ale mam 'ManyToManyRelation', więc kaskada usuwa tylko zapis, który mówi" wpis X i wersja Y należą do siebie ", ale nie sam rekord" BlogEntryRevision ". Może powinienem zrobić to za pomocą 'ForeignKey' w moim przypadku użycia ... W każdym razie, rozważ to pytanie jako ogólne pytanie dotyczące wielu kaskadowych usunięć. – AndiDog

8

miałem dokładnie ten przypadków użycia dzisiaj:

  • model Autor: może mieć kilka wpisów
  • Wzór aktu: może mieć kilku autorów

Do tego używam ManyToManyRelationship.

Mój przypadek użycia był następujący: jeśli usunę ostatni wpis konkretnego autora, to ten autor również powinien zostać usunięty.

Rozwiązaniem może być osiągnięty za pomocą 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

Dlaczego warto sprawdzić' i wystąpienie w authors.entries.all() '? Już trwają iteracje nad autorami w relacji instancji. –

+0

To dobre pytanie, nie jestem już pewien, dlaczego go dodałem. Prawdopodobnie jest to również możliwe bez czeku. – jessepeng