views:

661

answers:

4

How can I remove or change the verbose name of the default admin action "delete selected X item" in the Django admin panel?

+1  A: 

Not sure if this sort of monkey-patching is a good idea, but shoving this in one of my admin.py works for me:

from django.contrib.admin.actions import delete_selected
delete_selected.short_description = u'How\'s this for a name?'

This will change the verbose name for all your admin sites. If you want to change it just for one particular model's admin, I think you'll need to write a custom admin action.

Tested with Django version 1.1:

>>> import django
>>> django.VERSION
(1, 1, 0, 'beta', 1)
Dominic Rodger
seems a clever way for me it doesn't work. It simply removes the action box.
Hellnar
Huh. I tested it with Django 1.1 and it works fine. I put it right at the top (below the other import statements) of my admin.py.
Dominic Rodger
it is (1 , 1, 0,'final', 0) for me, maybe thats the reason :(
Hellnar
cpharmston
I doubt it - I guessed at that idea by looking at the source for trunk (specifically http://code.djangoproject.com/browser/django/trunk/django/contrib/admin/actions.py#L85).
Dominic Rodger
@cpharmston - thanks.
Dominic Rodger
alright, thanks for the suggestion :)
Hellnar
A: 

And how to override the delete_selected behaviour?

anibal
See my answer for how, generally, to add actions to an Admin class, but really, see the Django docs http://docs.djangoproject.com/en/dev/ref/contrib/admin/actions/ for more on how the actions work.
Joe Germuska
+2  A: 

You can disable the action from appearing with this code.

from django.contrib import admin
admin.site.disable_action('delete_selected')

If you chose, you could then restore it on individual models with this:

class FooAdmin(admin.ModelAdmin):
    actions = ['my_action', 'my_other_action', admin.actions.delete_selected]
Joe Germuska
A: 

In order to replace delete_selected I do the following:

Copy the function delete_selected from contrib/admin/actions.py to your admin.py and rename it. Also copy the template contrib/admin/templates/delete_selected_confirmation.html to your template directory and rename it. Mine looks like this:

def reservation_bulk_delete(modeladmin, request, queryset):
    """
    Default action which deletes the selected objects.
    This action first displays a confirmation page whichs shows all the
    deleteable objects, or, if the user has no permission one of the related
    childs (foreignkeys), a "permission denied" message.

    Next, it delets all selected objects and redirects back to the change list.
    """
    opts = modeladmin.model._meta
    app_label = opts.app_label

    # Check that the user has delete permission for the actual model
    if not modeladmin.has_delete_permission(request):
        raise PermissionDenied

    # Populate deletable_objects, a data structure of all related objects that
    # will also be deleted.

    # deletable_objects must be a list if we want to use '|unordered_list' in the template
    deletable_objects = []
    perms_needed = set()
    i = 0
    for obj in queryset:
        deletable_objects.append([mark_safe(u'%s: <a href="%s/">%s</a>' % (escape(force_unicode(capfirst(opts.verbose_name))), obj.pk, escape(obj))), []])
        get_deleted_objects(deletable_objects[i], perms_needed, request.user, obj, opts, 1, modeladmin.admin_site, levels_to_root=2)
        i=i+1

    # The user has already confirmed the deletion.
    # Do the deletion and return a None to display the change list view again.
    if request.POST.get('post'):
        if perms_needed:
            raise PermissionDenied
        n = queryset.count()
        if n:
            for obj in queryset:
                obj_display = force_unicode(obj)

                obj.delete()

                modeladmin.log_deletion(request, obj, obj_display)
            #queryset.delete()
            modeladmin.message_user(request, _("Successfully deleted %(count)d %(items)s.") % {
                "count": n, "items": model_ngettext(modeladmin.opts, n)
            })
        # Return None to display the change list page again.
        return None

    context = {
        "title": _("Are you sure?"),
        "object_name": force_unicode(opts.verbose_name),
        "deletable_objects": deletable_objects,
        'queryset': queryset,
        "perms_lacking": perms_needed,
        "opts": opts,
        "root_path": modeladmin.admin_site.root_path,
        "app_label": app_label,
        'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
    }

    # Display the confirmation page
    return render_to_response(modeladmin.delete_confirmation_template or [
        "admin/%s/%s/reservation_bulk_delete_confirmation.html" % (app_label, opts.object_name.lower()),
        "admin/%s/reservation_bulk_delete_confirmation.html" % app_label,
        "admin/reservation_bulk_delete_confirmation.html"
    ], context, context_instance=template.RequestContext(request))

As you can see I commented out

queryset.delete()

and rather use:

obj.delete()

That's not optimal yet - you should apply something to the entire queryset for better performance.

In admin.py I disable the default action delete_selected for the entire admin site:

admin.site.disable_action('delete_selected')

Instead I use my own function where needed:

class ReservationAdmin(admin.ModelAdmin):
    actions = [reservation_bulk_delete, ]

In my model I define the delete() function:

class Reservation(models.Model):
    def delete(self):
        self.status_server = RESERVATION_STATUS_DELETED
        self.save()
Googol