2009-09-24 60 views
31

我有一個模型:的Django模型:刪除()不會觸發

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

保存() - 方法被觸發,但刪除()不是。我使用最新的svn-Version(Django版本1.2 pre-alpha SVN-11593),並且關於文檔http://www.djangoproject.com/documentation/models/save_delete_hooks/這應該起作用。 任何想法?

+0

也許你沒有刪除任何東西?你能告訴我們你在調用delete()嗎? – artagnon 2009-09-24 14:10:33

+0

我試過了,只是刪除管理區中的一個項目,並沒有手動調用它。 – schneck 2009-09-24 14:11:35

回答

68

我想你可能正在使用管理員的批量刪除功能,並且正在運行管理員批量刪除方法不會調用delete()(請參閱相關ticket)的事實。

我已經通過編寫刪除模型的自定義管理操作了解了這一點。

如果您未使用管理員的批量刪除方法(例如,您點擊對象編輯頁面上的刪除按鈕),則會發生其他情況。

看到警告here

的「刪除選定的對象」行動 使用QuerySet.delete()效率 原因,其中有一個重要的 警告:你的模型delete()方法 不會被調用。

如果要覆蓋此行爲, 簡單的寫一個 完成刪除您的 優選的方式自定義操作 - 例如,通過調用 爲Model.delete()每個 選擇的項目。

有關批量刪除的更多背景信息,請參閱object deletion的文檔。

我定製的管理模式是這樣的:

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

是的,就是這樣,非常感謝。你能否簡短地解釋你的自定義管理方法是怎樣的? – schneck 2009-09-24 14:17:46

+0

@schneck - 當然可以! – 2009-09-24 14:19:34

+0

順便說一句 - 有可能是更完美的方式來完成它,但它的作品! – 2009-09-24 14:20:10

29

我知道這個問題是古老的,但我只是碰到了這一次想補充一點,你可以在你的代碼始終移到pre_delete或post_delete信號如下:

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" 

它適用於管理員的批量刪除操作(至少從1.3.1開始)。

+0

非常漂亮。只是想知道在django 1.4中有什麼改進嗎? – 2012-06-10 02:55:41

+0

thnx一噸!我使用post_delete來避免遞歸刪除。 – Babu 2012-09-11 13:05:44

5

管理員的批量操作調用queryset.delete()

您可以覆蓋查詢集的.delete()方法,即 ,因此它始終會對對象進行1x 1的刪除操作。例如:

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) 

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

的主要問題是,Django管理的批量刪除使用SQL,不是實例。刪除(),如其他地方所述。對於僅限管理員的解決方案,以下解決方案保留了Django管理員的「是否真的要刪除這些」插頁式廣告。然而,vdboor的解決方案是最普遍的。

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