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