I thought for whatever reason this would be easy to do, but I looked deeper and it appears there is no straightforward way to allow users to execute custom admin actions on the
Here's what I ended up doing.
First, I extended the change_view of the ModelAdmin object as follows:
def change_view(self, request, object_id, extra_context=None):
actions = self.get_actions(request)
if actions:
action_form = self.action_form(auto_id=None)
action_form.fields['action'].choices = self.get_action_choices(request)
else:
action_form = None
changelist_url = urlresolvers.reverse('admin:checkout_order_changelist')
return super(OrderAdmin, self).change_view(request, object_id, extra_context={
'action_form': action_form,
'changelist_url': changelist_url
})
Basically we're just gathering the data needed to populate the actions dropdown on the change view.
Then I just extended change_form.html for the model in question:
{% extends "admin/change_form.html" %}
{% load i18n adminmedia admin_list %}
{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/changelists.css" />
{% endblock %}
{% block object-tools %}
{{ block.super }}
<div id="changelist">
<form action="{{ changelist_url }}" method="POST">{% csrf_token %}
{% admin_actions %}
<input type="hidden" name="_selected_action" value="{{ object_id }}">
</form>
</div>
{% endblock %}
This is almost identical to how the admin actions section is outputted on the change list view. The main differences are: 1) I had to specify a URL for the form to post to, 2) instead of a checkbox to specify which object(s) should be changed, the value is set via a hidden form field, and 3) I included the CSS for the change list view, and stuck the actions in a div with id of #changelist
-- just so the box would look halfway decent.
Not a great solution, but it works okay and requires no additional configuration for additional actions you might add.
Here is update and improvement of this answer. It works with django 1.6 and redirects to where you came from.
class ActionInChangeFormMixin(object):
def response_action(self, request, queryset):
"""
Prefer http referer for redirect
"""
response = super(ActionInChangeFormMixin, self).response_action(request,
queryset)
if isinstance(response, HttpResponseRedirect):
response['Location'] = request.META.get('HTTP_REFERER', response.url)
return response
def change_view(self, request, object_id, extra_context=None):
actions = self.get_actions(request)
if actions:
action_form = self.action_form(auto_id=None)
action_form.fields['action'].choices = self.get_action_choices(request)
else:
action_form = None
extra_context=extra_context or {}
extra_context['action_form'] = action_form
return super(ActionInChangeFormMixin, self).change_view(request, object_id, extra_context=extra_context)
class MyModelAdmin(ActionInChangeFormMixin, ModelAdmin):
......
Template:
{% extends "admin/change_form.html" %}
{% load i18n admin_static admin_list admin_urls %}
{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static "admin/css/changelists.css" %}" />
{% endblock %}
{% block object-tools %}
{{ block.super }}
<div id="changelist">
<form action="{% url opts|admin_urlname:'changelist' %}" method="POST">{% csrf_token %}
{% admin_actions %}
<input type="hidden" name="_selected_action" value="{{ object_id }}">
</form>
</div>
{% endblock %}
What I did was create my own MYAPP/templates/admin/MYMODEL/change_form.html template:
{% extends "admin/change_form.html" %}
{% load i18n %}
{% block object-tools %}
{% if change %}{% if not is_popup %}
<ul class="object-tools">
<li><a href="{% url MY_COMMAND_VIEW original.id %}" class="historylink" >MY COMMAND</a></li>
<li><a href="history/" class="historylink">{% trans "History" %}</a></li>
{% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
</ul>
{% endif %}{% endif %}
{% endblock %}
So I basically only changed the block "object-tools" where the history-link and the "view on site"-link are. the rest of the original change_form.html remains untouched. BTW: "original.id" is the id of the model you are editing.