Add delete buttons to admin changelist

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
###   MODEL: Include something like this in your model   ###
class Person(models.Model):

    class Admin:
        list_display = ['name', 'select']

    name = models.CharField(maxlength=100)

    def select(self):
        return '<input type="checkbox" name="person" value="%s"/>' % self.id
    select.allow_tags = True
    select.short_description = ''



###   TEMPLATE: 'change_list.html'   ###
###   Put it in either admin/change_list.html, admin/<app>/<model>/change_list.html, etc   ###
{% extends "admin/change_list.html" %}

{% block result_list %}
<form action="delete/" method="post">
    {{ block.super }}
    {% for r in cl.get_query_set %}
        <input type="hidden" name="qs_obj" value="{{ r.id }}" id="qs_obj_{{ r.id }}">
    {% endfor %}
    <p>
        <input type="submit" name="delete_selected" id="id_delete_selected" value="Delete selected" />
        <input type="submit" name="delete_shown" id="id_delete_shown" value="Delete shown" />               
        <input type="submit" name="delete_all" id="id_delete_all" value="Delete all" /> 
    </p>
</form> 
{% endblock %}



###  VIEW: I just tossed this in 'admin.py' in the project root   ###
from django.contrib.admin.views.main import *

def delete(request, app_label, model_name):
    
    model = models.get_model(app_label, model_name)
    opts = model._meta  
    if model is None:
        raise Http404("App %r, model %r, not found" % (app_label, model_name))
    if not request.user.has_perm(app_label + '.' + model._meta.get_change_permission()):
        raise PermissionDenied  
    try:
        cl = ChangeList(request, model)
    except IncorrectLookupParameters:
        if ERROR_FLAG in request.GET.keys():
            return render_to_response('admin/invalid_setup.html', {'title': _('Database error')})
        return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1')

    if 'delete_selected' in request.POST and model_name in request.POST:
        deleted = []
        for obj in cl.get_query_set().filter(id__in=request.POST.getlist(model_name)):
            obj.delete()
            deleted.append('"%s"' % str(obj))
        request.user.message_set.create(message=_('The %(name)s %(obj)s were deleted successfully.') % {'name': opts.verbose_name_plural, 'obj': ", ".join(deleted)})

    if 'delete_shown' in request.POST and 'qs_obj' in request.POST:
        deleted = []
        for obj in cl.get_query_set().filter(id__in=request.POST.getlist('qs_obj')):
            obj.delete()
            deleted.append('"%s"' % str(obj))
        request.user.message_set.create(message=_('The %(name)s %(obj)s were deleted successfully.') % {'name': opts.verbose_name_plural, 'obj': ", ".join(deleted)})
        
    if 'delete_all' in request.POST and cl.get_query_set().count() > 0:
        for obj in cl.get_query_set():
            obj.delete()
        request.user.message_set.create(message=_('All %(name)s were deleted successfully.') % {'name': opts.verbose_name_plural})      
    
    return HttpResponseRedirect('..')
change_list = staff_member_required(never_cache(change_list))



###   URLS: Add this line to your urls BEFORE the normal admin urls line   ###
    ('^admin/([^/]+)/([^/]+)/delete/$', 'admin.delete'),

More like this

  1. Javascript constraints in admin app and fieldsets to tabs (jquery) by jpic 5 years, 1 month ago
  2. List all errors in a form +bootstrap highlighting by ibrahimlawal 1 year, 7 months ago
  3. Dynamic tabular inlines with optional drag-n-drop sorting by Aneon 4 years, 11 months ago
  4. Admin Apps Names Translation by Nad/ 4 years, 3 months ago
  5. Dynamically adding forms to a formset with jQuery by elo80ka 5 years, 1 month ago

Comments

bfrederi (on October 1, 2007):

On line 54, delete_selected seems to be failing on the condition "model_name in request.POST:". model_name doesn't appear anywhere in request.POST when I attempt to remove an item.

#

bfrederi (on October 1, 2007):

Ok, sorry I see what you did now: class Person(models.Model): return '[HTML_REMOVED]' % self.id

You named the input checkbox the same as the model. My mistake.

#

bfrederi (on October 3, 2007):

I added a bit to your snippet to allow the user to select all and un-select all by use of a check box next to the Delete field and some Jquery. Here is the Jquery: [HTML_REMOVED] $("#checkboxall").click(function(){ var checked_status = this.checked; $("input[@name=person]").each(function(){ this.checked = checked_status; }); }); [HTML_REMOVED] Then add this to the short description: [HTML_REMOVED] select.short_description = 'Delete[HTML_REMOVED][HTML_REMOVED]' [HTML_REMOVED] I thought this would help some people. Great snippet. Very useful.

#

bfrederi (on October 3, 2007):

Sorry, short description should look like this: select.short_description = 'Delete&nbsp;<input type="checkbox" id="checkboxall" name="check_all" value="Check All" />'

#

bfrederi (on January 25, 2008):

Lines 59, 66, and 71 generate this error with version .97: "global name '_' is not defined" I'm not very good with Python, so I'm not sure how to fix this. Any suggestions?

Also with my comment on using jQuery in short_description, you will have to use mark_safe() on that now, since autoescape defaults to on in .97. (from django.utils.safestring import mark_safe). Otherwise the html won't render properly.

#

bfrederi (on March 3, 2008):

I don't know python well enough to know why those lines were causing errors, but I changed it to: request.user.message_set.create(message="The %(name)s %(obj)s were deleted successfully." % {'name': opts.verbose_name_plural, 'obj': ", ".join(deleted)}) instead and it works.

#

(Forgotten your password?)