Login

Add buttons in admin forms

Author:
mamat
Posted:
September 3, 2008
Language:
Python
Version:
1.0
Tags:
admin buttons
Score:
5 (after 5 ratings)

A subclass of this admin will let you add buttons (like history) in the change view of an entry.

 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
class ButtonableModelAdmin(admin.ModelAdmin):
   """
   A subclass of this admin will let you add buttons (like history) in the
   change view of an entry.

   ex.
   class FooAdmin(ButtonableModelAdmin):
      ...

      def bar(self, obj):
         obj.bar()
      bar.short_description='Example button'

      buttons = [ bar ]

   you can then put the following in your admin/change_form.html template:

      {% block object-tools %}
      {% if change %}{% if not is_popup %}
      <ul class="object-tools">
      {% for button in buttons %}
         <li><a href="{{ button.func_name }}/">{{ button.short_description }}</a></li>
      {% endfor %}
      <li><a href="history/" class="historylink">History</a></li>
      {% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">View on site</a></li>{% endif%}
      </ul>
      {% endif %}{% endif %}
      {% endblock %}

   """
   buttons=[]

   def change_view(self, request, object_id, extra_context={}):
      extra_context['buttons']=self.buttons
      return super(ButtonableModelAdmin, self).change_view(request, object_id, extra_context)

   def __call__(self, request, url):
      if url is not None:
         import re
         res=re.match('(.*/)?(?P<id>\d+)/(?P<command>.*)', url)
         if res:
            if res.group('command') in [b.func_name for b in self.buttons]:
               obj = self.model._default_manager.get(pk=res.group('id'))
               getattr(self, res.group('command'))(obj)
               return HttpResponseRedirect(request.META['HTTP_REFERER'])

      return super(ButtonableModelAdmin, self).__call__(request, url)

More like this

  1. Add delete button in admin cp by cschand 6 years, 11 months ago
  2. Improved Button Admin by kunitoki 3 years ago
  3. URLField admin widget with View Link button by ungenio41 4 years, 10 months ago
  4. Add admin edit form buttons (work with django 1.6) by shoreward 1 year, 2 months ago
  5. Admin Save and view next button by ungenio41 4 years, 10 months ago

Comments

donspaulding (on October 17, 2008):

Just what I was looking for! I'm definitely adding this to my toolbelt.

Two things:

  1. On line 44, I found that passing the request, and expecting an HttpResponse gave me more 'view-like' functionality inside my method.
  2. It seems to have 3-space indentation.

Thanks for this!

#

caesarmv (on May 22, 2010):

Rechecked on django 1.2. It needs additional string here.

from django.utils.functional import update_wrapper

Work code below:

class ButtonableModelAdmin(admin.ModelAdmin):

buttons=[]

def change_view(self, request, object_id, extra_context={}): extra_context['buttons']=self.buttons if '/' in object_id: object_id = object_id[:object_id.find('/')] return super(ButtonableModelAdmin, self).change_view(request, object_id, extra_context)

def button_view_dispatcher(self, request, url): if url is not None: import re res=re.match('(./)?(?P[HTML_REMOVED]\d+)/(?P[HTML_REMOVED].)', url) if res: if res.group('command') in [b.func_name for b in self.buttons]: obj = self.model._default_manager.get(pk=res.group('id')) getattr(self, res.group('command'))(obj) return HttpResponseRedirect(request.META['HTTP_REFERER'])

  return super(ButtonableModelAdmin, self).__call__(request, url)

def get_urls(self):

   from django.conf.urls.defaults import patterns, url
   from django.utils.functional import update_wrapper

   def wrap(view):
       def wrapper(*args, **kwargs):
           return self.admin_site.admin_view(view)(*args, **kwargs)
       return update_wrapper(wrapper, view)

   urlpatterns = patterns('',
       url(r'^(.+)/$',
           wrap(self.button_view_dispatcher),)
   ) + super(ButtonableModelAdmin, self).get_urls()
   return urlpatterns

#

arvid (on July 6, 2010):

Last corrections. Tested with Django 1.2.1.

from django.contrib import admin from django.http import HttpResponseRedirect

class ButtonableModelAdmin(admin.ModelAdmin):

buttons=()

def change_view(self, request, object_id, extra_context={}): 
    extra_context['buttons']=self.buttons 
    return super(ButtonableModelAdmin, self).change_view(request, object_id, extra_context)

def button_view_dispatcher(self, request, object_id, command): 
    obj = self.model._default_manager.get(pk=object_id) 
    return getattr(self, command)(request, obj)  \
        or HttpResponseRedirect(request.META['HTTP_REFERER'])

def get_urls(self):

    from django.conf.urls.defaults import patterns, url
    from django.utils.functional import update_wrapper

    def wrap(view):
        def wrapper(*args, **kwargs):
            return self.admin_site.admin_view(view)(*args, **kwargs)
        return update_wrapper(wrapper, view)

    info = self.model._meta.app_label, self.model._meta.module_name

    return patterns('',
        *(url(r'^(\d+)/(%s)/$' % but.func_name, wrap(self.button_view_dispatcher)) for but in self.buttons)
    ) + super(ButtonableModelAdmin, self).get_urls()

#

dodgyville (on November 1, 2011):

I found this snippet (and arvid's 1.2 updates) very helpful. However, it does not work in 1.3 due to the the change to "Callables in templates" (see the 1.3 changelist docs). This means the items in buttons are being evaluated in the template for loop, so func_name and short_description are not available since "button" is now automatically "button()".

My quick workaround:

In FooAdmin: Instead of:
buttons = [bar]
use:
buttons = [(bar.func_name, bar.short_description), ]

In arvid's get_urls use but[0] instead of but.func_name

And in the template use button.0 and button.1 instead of button.func_name and button.short_description

#

nanvel (on December 5, 2013):

For django v 1.4 (form_url argument was added to change_view):

class ButtonableModelAdmin(admin.ModelAdmin):

    buttons = []

    def change_view(self, request, object_id, extra_context={}): 
        extra_context['buttons'] = self.buttons
        return super(ButtonableModelAdmin, self).change_view(
                request=request, object_id=object_id, extra_context=extra_context)

    def button_view_dispatcher(self, request, object_id, command): 
        obj = self.model._default_manager.get(pk=object_id) 
        return getattr(self, command)(request, obj) \
                or HttpResponseRedirect(request.META['HTTP_REFERER'])

    def get_urls(self):

        from django.conf.urls.defaults import patterns, url
        from django.utils.functional import update_wrapper

        def wrap(view):
            def wrapper(*args, **kwargs):
                return self.admin_site.admin_view(view)(*args, **kwargs)
            return update_wrapper(wrapper, view)

        info = self.model._meta.app_label, self.model._meta.module_name

        return patterns('',
            *(url(r'^(\d+)/(%s)/$' % but[0], wrap(self.button_view_dispatcher)) for but in self.buttons)
        ) + super(ButtonableModelAdmin, self).get_urls()

#

Please login first before commenting.