2009-09-24 11 views
31

Mam model:Django model: delete() nie spowodowało

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

SAVE() - metoda jest wyzwalany, ale delete() nie jest. Używam najnowszej wersji svn (Django wersja 1.2 pre-alpha SVN-11593), a odnośnie dokumentacji pod adresem http://www.djangoproject.com/documentation/models/save_delete_hooks/ powinno to działać. Jakieś pomysły?

+0

Być może nic nie usuwasz? Czy możesz pokazać nam, do czego wywołuje metodę delete()? – artagnon

+0

Próbowałem go po prostu usuwając element w obszarze administracyjnym i nie wywoływał go ręcznie. – schneck

Odpowiedz

68

Myślę, że prawdopodobnie korzystasz z funkcji usuwania zbiorczego administratora i masz pewność, że metoda usuwania zbiorczego administratora nie wywołuje funkcji delete() (zobacz powiązany kod ticket).

Mam już za sobą to w przeszłości, pisząc niestandardową akcję administratora do usuwania modeli.

Jeśli nie używasz zbiorczej metody usuwania administratora (np. Klikasz przycisk usuwania na stronie edycji obiektu), to dzieje się coś innego.

Patrz ostrzeżenie here:

„Usuń wybrane obiekty” działanie używa QuerySet.delete() na efektywność powodów, które odgrywa ważną zastrzeżenie: Ten model jest delete() metody nie zostanie wywołana.

Jeśli chcesz to zmienić, prostu napisać akcję niestandardową, która osiągnie, usunięcie w korzystny sposób - na przykład przez nazywając Model.delete() dla każdej z wybranych pozycji z .

Więcej informacji na temat usuwania zbiorczego, można znaleźć w dokumentacji na stronie object deletion.

Mam zwyczaj modelu Administrator wygląda następująco:

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

tak, to tyle, wielkie dzięki. czy możesz krótko wyjaśnić, jak wygląda Twoja niestandardowa metoda administratora? – schneck

+0

@schneck - Oczywiście! –

+0

btw - mogą być bardziej eleganckie sposoby na osiągnięcie tego, ale działa! –

29

Wiem, że to pytanie jest stare, ale ja po prostu wpadł na to jeszcze raz i chciałem dodać, że zawsze można przenieść swój kod do pre_delete lub post_delete sygnał tak:

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" 

współpracuje z luzem admin usunąć za akcję (co najmniej od 1.3.1).

+0

bardzo śliskie. Zastanawiam się, czy jest jakaś poprawa z tym w django 1.4? –

+0

thnx a ton! Użyłem post_delete, aby uniknąć rekurencyjnych usunięć. – Babu

5

Zbiorcza akcja administratora wywołuje queryset.delete().

Można przesłonić metodęzestawu zapytań, , więc zawsze wykonuje usuwanie obiektów 1-na-1. Na przykład:

w 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) 

W 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

Głównym problemem jest to, że większość Django admin usunąć wykorzystuje SQL, a nie instancję .delete(), jak zaznaczono w innym miejscu. W przypadku rozwiązania wyłącznie administracyjnego następujące rozwiązanie zachowuje "pełnoekranową reklamę administratora Django" na pewno chcesz usunąć ". Rozwiązanie vdboor jest jednak najbardziej ogólne.

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): 
    ...