Skip to main content
 首页 » 编程设计

python之Django:如何在管理表单中的“历史记录”按钮旁边添加一个操作按钮之干净利落

2025年05月04日42powertoolsteam

在我的Tournament 管理员中,我想添加一个按钮来创建匹配结构,这取决于之前注册的竞争Team。这是一个涉及单个锦标赛的 Action ,没有“结果”——它只是对数据库进行了适度复杂的更新。

我将功能实现为 admin action ,它运行良好,而且似乎是实现该功能的最佳方式。但是,大多数时候,用户会希望从锦标赛页面而不是锦标赛列表中执行该操作。

This答案显示了如何将管理操作表单添加到相关页面,但我不需要下拉列表,其中还包括“删除”操作;一个简单的按钮会更好。

This问题是关于添加按钮(虽然它是用于列表页面),但正确的样式只适用于 a href 标签,不适用于按钮;我正在执行一个 Action ,而不是显示一个新文档。我想避免复制相关的 CSS 以使用表单提交按钮。我还想避免对操作名称进行硬编码,或者通常重复管理操作下拉菜单会为我推断的内容。

所以我的具体问题是:

  • 获得正确样式的最佳方法是什么?我是否必须使用 a 标记并通过 JavaScript 提交,还是有更好的方法?
  • 我怎样才能最好地复制 this 的逻辑?通过使用 {% admin_actions %} 获得答案(生成带有下拉列表的表单)?也就是说,检索管理操作,使用它们的描述等显示它们,但作为按钮?

相关代码如下:

admin.py

# from https://stackoverflow.com/a/20379270/371191 
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, form_url='', 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 TournamentAdmin(ActionInChangeFormMixin, admin.ModelAdmin): 
    actions = ['setup_matches'] 
 
    def setup_matches(self, request, queryset): 
        for tournament in queryset: 
            try: 
                tournament.setup() 
            except ValueError as ex: 
                messages.error(request, 'Could not update %s: %s'  % (tournament, ex)) 
            else: 
                messages.success(request, 'Updated %s' % (tournament,)) 
 
    setup_matches.short_description = 'Setup matches for selected tournaments' 

change_form.py

{% extends "admin/change_form.html" %} 
{% load i18n admin_urls %} 
 
{% block object-tools-items %} 
    <li> 
        <form action="{% url opts|admin_urlname:'changelist' %}" method="POST">{% csrf_token %} 
            <input type="hidden" name="action" value="setup_matches"> 
            <input type="hidden" name="_selected_action" value="{{ object_id }}"> 
            <button value="0" name="index" title="Setup matches for selected tournaments" type="submit">Setup matches</button> 
        </form> 
    </li> 
    {{ block.super }} 
{% endblock %} 

请您参考如下方法:

ActionInChangeFormMixin.change_view() 中的大部分代码是专门为下拉列表设置的代码,因此与上面显示的模板一起使用时是死代码。逻辑发生在这里:

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

action_form创建实际的表单 - 我们不想渲染它。 get_action_choices填充 <select>使用元组作为选项。

为了尽可能灵活,我将介绍一种新方法,它只检索我们想要显示的 Action 。另外,让我们去掉不必要的代码(新代码的灵感来自 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) 

在TournamentAdmin ,然后我们可以过滤我们想要看到的 Action 。在这种情况下,我不想显示批量删除操作的按钮:

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

change_form.html现在需要一些逻辑来呈现相关按钮:

{% 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 %} 

这使用 JavaScript 来提交表单;我认为没有更简洁的方法来获得正确的样式。