2009-09-24 10 views
31

Ho un modello:Django modello delete() non innescato

class MyModel(models.Model): 
... 
    def save(self): 
     print "saving" 
     ... 
    def delete(self): 
     print "deleting" 
     ... 

Il save() - Metodo viene attivato, ma il delete() non lo è. Io uso l'ultima versione di svn (versione di Django 1.2 pre-alpha SVN-11593), e per quanto riguarda la documentazione allo http://www.djangoproject.com/documentation/models/save_delete_hooks/ questo dovrebbe funzionare. Qualche idea?

+0

Forse non stai cancellando nulla? Puoi mostrarci cosa stai invocando delete() in? – artagnon

+0

L'ho provato semplicemente eliminando un elemento nell'area di amministrazione e non l'ho chiamato manualmente. – schneck

risposta

68

Penso che probabilmente stai utilizzando la funzione di eliminazione di massa dell'amministratore e ci stiamo imbattendo nel fatto che il metodo di eliminazione di massa dell'amministratore non chiama delete() (vedi il relativo ticket).

L'ho già fatto in passato scrivendo un'azione di amministrazione personalizzata per l'eliminazione di modelli.

Se non si utilizza il metodo di eliminazione di massa dell'amministratore (ad esempio, si fa clic sul pulsante Elimina nella pagina di modifica dell'oggetto), succede qualcos'altro.

visualizzato l'avviso here:

L'azione “eliminare gli oggetti selezionati” usa QuerySet.delete() per l'efficienza motivi, che ha un importante avvertimento: il metodo del vostro modello delete() non sarà chiamato .

Se si desidera sovrascrivere questo comportamento, è sufficiente scrivere un'azione personalizzata che compie l'eliminazione nella vostra maniera preferita - ad esempio, chiamando Model.delete() per ciascuno dei elementi selezionati.

Per ulteriori informazioni sulla cancellazione di massa, vedere la documentazione su object deletion.

Il mio modello di amministrazione personalizzato assomiglia a questo:

from photoblog.models import PhotoBlogEntry 
from django.contrib import admin  

class PhotoBlogEntryAdmin(admin.ModelAdmin): 
    actions=['really_delete_selected'] 

    def get_actions(self, request): 
     actions = super(PhotoBlogEntryAdmin, self).get_actions(request) 
     del actions['delete_selected'] 
     return actions 

    def really_delete_selected(self, request, queryset): 
     for obj in queryset: 
      obj.delete() 

     if queryset.count() == 1: 
      message_bit = "1 photoblog entry was" 
     else: 
      message_bit = "%s photoblog entries were" % queryset.count() 
     self.message_user(request, "%s successfully deleted." % message_bit) 
    really_delete_selected.short_description = "Delete selected entries" 

admin.site.register(PhotoBlogEntry, PhotoBlogEntryAdmin) 
+0

sì, è tutto, grazie mille. potresti spiegare brevemente come appare il tuo metodo di amministrazione personalizzato? – schneck

+0

@schneck: certo! –

+0

btw - potrebbero esserci modi più eleganti per realizzarlo, ma funziona! –

29

So che questa domanda è antica, ma ho appena imbattuto in questo nuovo e volevo aggiungere che si può sempre spostare il codice per un pre_delete o segnale post_delete in questo modo:

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

@receiver(pre_delete, sender=MyModel) 
def _mymodel_delete(sender, instance, **kwargs): 
    print "deleting" 

funziona con massa del amministratore cancellare azione (almeno a partire da 1.3.1).

+0

molto lucido. Mi chiedo solo se c'è qualche miglioramento con questo in django 1.4? –

+0

thnx a ton! Ho usato post_delete per evitare le eliminazioni ricorsive. – Babu

5

L'azione di massa dell'amministratore chiama queryset.delete().

È possibile sovrascrivere il metodo .delete() del set di query, in modo che effettui sempre una cancellazione 1 per 1 di oggetti. Per esempio:

in managers.py:

from django.db import models 
from django.db.models.query import QuerySet 

class PhotoQueryMixin(object): 
    """ Methods that appear both in the manager and queryset. """ 
    def delete(self): 
     # Use individual queries to the attachment is removed. 
     for photo in self.all(): 
      photo.delete() 

class PhotoQuerySet(PhotoQueryMixin, QuerySet): 
    pass 

class PhotoManager(PhotoQueryMixin, models.Manager): 
    def get_query_set(self): 
     return PhotoQuerySet(self.model, using=self._db) 

In models.py:

from django.db import models 

class Photo(models.Model): 
    image = models.ImageField(upload_to='images') 

    objects = PhotoManager() 

    def delete(self, *args, **kwargs): 
     # Note this is a simple example. it only handles delete(), 
     # and not replacing images in .save() 
     super(Photo, self).delete(*args, **kwargs) 
     self.image.delete() 
3

Il problema principale è che grosso della amministrazione Django cancellare utilizza SQL, non esempio .delete(), come indicato altrove. Per una soluzione solo admin, la seguente soluzione preserva l'interstitial dell'amministratore di Django "vuoi veramente eliminarli". La soluzione di vdboor è la più generale, comunque.

from django.contrib.admin.actions import delete_selected 

class BulkDeleteMixin(object): 
    class SafeDeleteQuerysetWrapper(object): 
     def __init__(self, wrapped_queryset): 
      self.wrapped_queryset = wrapped_queryset 

     def _safe_delete(self): 
      for obj in self.wrapped_queryset: 
       obj.delete() 

     def __getattr__(self, attr): 
      if attr == 'delete': 
       return self._safe_delete 
      else: 
       return getattr(self.wrapped_queryset, attr) 

     def __iter__(self): 
      for obj in self.wrapped_queryset: 
       yield obj 

     def __getitem__(self, index): 
      return self.wrapped_queryset[index] 

     def __len__(self): 
      return len(self.wrapped_queryset) 

    def get_actions(self, request): 
     actions = super(BulkDeleteMixin, self).get_actions(request) 
     actions['delete_selected'] = (BulkDeleteMixin.action_safe_bulk_delete, 'delete_selected', ugettext_lazy("Delete selected %(verbose_name_plural)s")) 
     return actions 

    def action_safe_bulk_delete(self, request, queryset): 
     wrapped_queryset = BulkDeleteMixin.SafeDeleteQuerysetWrapper(queryset) 
     return delete_selected(self, request, wrapped_queryset) 


class SomeAdmin(BulkDeleteMixin, ModelAdmin): 
    ...