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.