[Django]-Django: how to add an action button next to the History button in an admin form – cleanly?

3👍

Most of the code in ActionInChangeFormMixin.change_view() is specifically setup code for the dropdown list, and thus dead code when used with the template shown above. The logic happens here:

action_form = self.action_form(auto_id=None)
action_form.fields['action'].choices = self.get_action_choices(request)

action_form creates the actual form – which we don’t want to render anyway. get_action_choices populates the <select> with tuples to use as options.

To be as flexible as possible, I’ll introduce a new method, which retrieves only the actions we want to show. Also, let’s get rid of the unnecessary code (the new code is inspired by get_action_choices):

class ActionInChangeFormMixin(object):
    # ...

    def get_change_actions(self, request):
        return self.get_actions(request)

    def change_view(self, request, object_id, form_url='', extra_context=None):
        actions = self.get_change_actions(request) or OrderedDict()
        extra_context = extra_context or {}
        extra_context['change_actions'] = [(name, description % admin.utils.model_format_dict(self.opts))
                                           for func, name, description in six.itervalues(actions)]
        return super(ActionInChangeFormMixin, self).change_view(request, object_id, extra_context=extra_context)

In TournamentAdmin, we can then filter which actions we want to see. In this case, I don’t want to show a button for the bulk delete action:

def get_change_actions(self, request):
    result = self.get_actions(request)
    del result['delete_selected']
    return result

change_form.html now needs some logic to render the relevant buttons:

{% extends "admin/change_form.html" %}
{% load i18n admin_urls %}

{% block object-tools-items %}
    {% for action_name, action_description in change_actions %}
    <li>
        <form id="action_{{ action_name }}" action="{% url opts|admin_urlname:'changelist' %}" method="POST">{% csrf_token %}
            <input type="hidden" name="action" value="{{ action_name }}">
            <input type="hidden" name="_selected_action" value="{{ object_id }}">
            <a href="#" onclick="document.getElementById('action_{{ action_name }}').submit(); return false;" title="{{  action_description }}">{{  action_description }}</a>
        </form>
    </li>
    {% endfor %}
    {{ block.super }}
{% endblock %}

This uses JavaScript for submitting the form; I don’t think there’s a cleaner way to get the styling right.

Leave a comment