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)
Comments
Just what I was looking for! I'm definitely adding this to my toolbelt.
Two things:
Thanks for this!
#
This snippet is out of date, I modified it to work with django 1.1.1
#
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'])
def get_urls(self):
#
Last corrections. Tested with Django 1.2.1.
from django.contrib import admin from django.http import HttpResponseRedirect
class ButtonableModelAdmin(admin.ModelAdmin):
#
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
#