2010-10-14 12 views
14

Utilizzando i seguenti modelli correlati (un blog può avere revisioni multiple):Django - Cascade delezione 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) 
    [...] 

Come posso dire a Django per eliminare tutti i relativi BlogEntryRevision s quando il corrispondente BlogEntry è soppressa? Il valore predefinito sembra essere quello di mantenere gli oggetti in una relazione molti-a-molti se un oggetto del lato "altro" viene eliminato. Un modo per farlo - preferibilmente senza annullare il valore BlogEntry.delete?

risposta

10

Penso che tu sia equivoco la natura di un rapporto ManyToMany. Parli di "il corrispondente BlogEntry" che viene cancellato. Ma il punto di un ManyToMany è che ogni BlogEntryRevision ha più BlogEntries ad esso correlati. (E, ovviamente, ogni BlogEntry ha più BlogEntryRevisions, ma lo sai già.)

Dai nomi che hai usato, e dal fatto che vuoi questa funzionalità di cancellazione a cascata, penso che staresti meglio con un ForeignKey standard da BlogEntryRevision a BlogEntry. Finché non imposti null=True su quella ForeignKey, le eliminazioni verranno annullate - quando BlogEntry viene eliminato, anche tutte le Revisioni saranno.

+2

Sì, ho ammesso questo errore nel commento all'altra risposta. Per completezza: supponendo un caso d'uso che ha più senso con 'ManyToManyRelation', quale sarebbe un buon modo per eliminare a cascata le eliminazioni? Funziona l'approccio della risposta cancellata di Gabi Purcaru? – AndiDog

+2

Le eliminazioni si sovrappongono a '' null = True'' –

+0

Il comportamento di eliminazione in cascata su 'ForeignKey's può essere controllato con [' on_delete'] (https://docs.djangoproject.com/en/dev/ref/models/ campi/# django.db.models.ForeignKey.on_delete) (ma per default 'CASCADE') – meshy

2

è possibile utilizzare un custom model manager, ma la documentazione sembra indicare che essa does do something like this already e non posso ricordare esattamente che cosa questo significa:

Il metodo delete, convenientemente, è chiamato delete(). Questo metodo elimina immediatamente l'oggetto e ha nessun valore restituito. Esempio:

e.delete() 

È possibile anche eliminare gli oggetti alla rinfusa. Ogni QuerySet ha un metodo delete() , che elimina tutti i membri di QuerySet.

Ad esempio, questa operazione elimina tutte Entry oggetti con un anno pub_date del 2005:

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

tenere presente che tale volontà, ogni volta che possibile, essere eseguito esclusivamente in SQL, e quindi la cancellazione () i metodi di istanze di singoli oggetti non saranno chiamati durante il processo . Se hai fornito un metodo personalizzato delete() su una classe del modello e vuoi assicurarti che venga chiamato, devi "manualmente" eliminare le istanze di quel modello (ad esempio, tramite iterando su un QuerySet e chiamando delete() su ogni oggetto singolarmente) anziché utilizzare il metodo bulk delete() di un QuerySet.

Quando Django elimina un oggetto, emula il comportamento del SQL vincolo ON DELETE CASCADE - in altre parole, tutti gli oggetti che hanno avuto chiavi esterne indicando l'oggetto da eliminabili verranno eliminati insieme con it. Per esempio:

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

Destra, eliminazioni a cascata sono il valore predefinito per 1: N (ForeignKey' ') Relazioni come' Blog 1: N Entry' nella documentazione citata. Ma ho un 'ManyToManyRelation', quindi la cascata elimina solo il record che dice" la voce X e la revisione Y appartengono insieme "ma non il record" BlogEntryRevision' stesso. Forse dovrei farlo con 'ForeignKey' nel mio caso d'uso ... Comunque, considera la domanda come una domanda generica sulle eliminazioni a cascata molti-a-molti. – AndiDog

8

Ho avuto questo esatto caso d'uso oggi:

  • Modello Autore: può avere più voci
  • modello entry: può avere diversi autori

Per questo, sto usando un ManyToManyRelationship.

Il mio caso d'uso era: se cancello l'ultima voce di un particolare autore, allora anche questo autore dovrebbe essere cancellato.

La soluzione può essere ottenuto utilizzando il 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

Perché controllare' e istanza in authors.entries.all() '? Stai già iterando sugli autori nella relazione dell'istanza. –

+0

Questa è una buona domanda, non sono più sicuro perché l'ho aggiunto. Probabilmente è anche possibile senza il controllo. – jessepeng