2009-10-08 8 views
15

Ho un problema perché sto cancellando un widget usando some_widget_instance.delete(). Ho anche un modello chiamato WidgetFile con un metodo override delete() in modo che io possa cancellare i file dal mio disco rigido quando un WidgetFile viene cancellato. Il problema che sto avendo è che se eliminare un Widget, e ha WidgetFiles ad essa collegati in questo modo:Come sovrascrivere delete() su un modello e farlo funzionare con le relative eliminazioni

class WidgetFile(models.Model): 

    widget = models.ForeignKey(Widget) 

Beh, quando elimino che Widget, è WidgetFiles vengono eliminati, ma il metodo delete() doesn Si innesca e fa il mio disco extra. Ogni aiuto è molto apprezzato.

+0

Questo problema è sorto perché quando un widget viene eliminato non attiva il metodo delete() su ciascuna delle sue dipendenze (classi che hanno un riferimento a chiave esterna ad esso). Elimina semplicemente gli oggetti correlati dal DB. Questo lo rende più efficiente ma ovviamente porta a problemi come questo. – orokusaki

risposta

24

ho capito. Ho appena messo questo su quel modello Widget:

def delete(self): 
    files = WidgetFile.objects.filter(widget=self) 
    if files: 
     for file in files: 
      file.delete() 
    super(Widget, self).delete() 

Questo scatenò il metodo delete necessario() su ciascuno degli oggetti correlati, innescando così il mio file personalizzato eliminazione di codice. È più costoso del database sì, ma quando si sta tentando di eliminare i file su un disco rigido in ogni caso, non è una così grande spesa per colpire il db un paio di volte in più.

+0

Non è chiaro il motivo per cui pensi che Celery o cron non incontrino una situazione simile in cui il file da eliminare potrebbe essere già aperto per un'operazione di lettura/scrittura da un altro processo. In entrambi i casi, dovresti scrivere il codice per gestire il caso speciale. –

+2

Ho rimosso quel pezzettino ... 4,5 anni fa ho pensato che potesse essere una buona idea, ma non ne sono del tutto sicuro. – orokusaki

1

È some_widget_instance e istanza di Widget o di WidgetFile? Perché se è un'istanza di Widget non otterrà la tua funzione personalizzata delete(), che si trova nella classe WidgetFile.

1

Questo sembra solo senso pieno se un Widget è collegato a un WidgetFile esattamente. In tal caso è necessario utilizzare un OneToOneField

da On-to-one examples:

# Delete the restaurant; the waiter should also be removed 
>>> r = Restaurant.objects.get(pk=1) 
>>> r.delete() 
+0

è vero, ma Django esegue un'eliminazione di massa a livello di database su tutti i camerieri senza attivare ciascuno dei propri metodi di eliminazione, che è meno costoso, ma anche meno convenzionale. – orokusaki

1

Giusto per gettare un possibile modo per aggirare questo problema: pre-deletesignal. (Non implica in alcun modo che non ci sia una soluzione reale.)

50

che sto facendo la stessa cosa e notato una pepita in Django documenti a cui dovresti pensare.

Overriding predefined model methods

Override Elimina noti che il metodo delete() per un oggetto non è necessariamente chiamato quando l'eliminazione di oggetti collettivamente utilizzando un QuerySet. Per garantire che la logica di eliminazione personalizzata venga eseguita, è possibile utilizzare i segnali pre_delete e/o post_delete.

Questo significa che il frammento non sempre fare quello che vuoi. L'utilizzo di Signals è un'opzione migliore per gestire le eliminazioni.

sono andato con il seguente:

import shutil 
from django.db.models.signals import pre_delete 
from django.dispatch import receiver 

@receiver(pre_delete) 
def delete_repo(sender, instance, **kwargs): 
    if sender == Set: 
     shutil.rmtree(instance.repo) 
0

Da Django 1.9, se solo definire on_delete=models.CASCADE per il campo, rimuoverà tutti gli oggetti correlati su Elimina.

+0

Questo non è giusto. La domanda non riguarda le eliminazioni a cascata. Si tratta di garantire che vengano chiamati i metodi 'delete' correlati. – orokusaki

Problemi correlati