Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django model: delete() not triggered

I have a model:

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

The save()-Method is triggered, but the delete() is not. I use the latest svn-Version (Django version 1.2 pre-alpha SVN-11593), and concerning the documentation at http://www.djangoproject.com/documentation/models/save_delete_hooks/ this should work. Any ideas?

like image 261
schneck Avatar asked Sep 24 '09 14:09

schneck


3 Answers

I think you're probably using the admin's bulk delete feature, and are running into the fact that the admin's bulk delete method doesn't call delete() (see the related ticket).

I've got round this in the past by writing a custom admin action for deleting models.

If you're not using the admin's bulk delete method (e.g. you're clicking the delete button on the object's edit page) then something else is going on.

See the warning here:

The “delete selected objects” action uses QuerySet.delete() for efficiency reasons, which has an important caveat: your model’s delete() method will not be called.

If you wish to override this behavior, simply write a custom action which accomplishes deletion in your preferred manner – for example, by calling Model.delete() for each of the selected items.

For more background on bulk deletion, see the documentation on object deletion.

My custom admin model looks like this:

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)
like image 132
Dominic Rodger Avatar answered Oct 15 '22 05:10

Dominic Rodger


I know this question is ancient, but I just ran into this again and wanted to add that you can always move your code to a pre_delete or post_delete signal like so:

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

It works with the admin's bulk delete action (at least as of 1.3.1).

like image 23
David Bennett Avatar answered Oct 15 '22 06:10

David Bennett


The bulk action of the admin calls queryset.delete().

You could override the .delete() method of the queryset, so it always does a 1-by-1 deletion of objects. For example:

in managers.py:

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

class PhotoQuerySet(QuerySet):
    """ 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()

In models.py:

from django.db import models

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

    objects = PhotoQuerySet.as_manager()

    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()
like image 9
vdboor Avatar answered Oct 15 '22 06:10

vdboor