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

Posted

技术标签:

【中文标题】Django:如何在管理表单中的“历史记录”按钮旁边添加一个操作按钮 - 干净利落?【英文标题】:Django: how to add an action button next to the History button in an admin form - cleanly? 【发布时间】:2016-01-04 14:52:51 【问题描述】:

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

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

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

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

所以我的具体问题是:

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

相关代码如下:

admin.py

# from https://***.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 %

【问题讨论】:

可以添加按钮吗? 【参考方案1】:

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 使用元组填充 &lt;select&gt; 以用作选项。

为了尽可能灵活,我将介绍一种新方法,它只检索我们想要显示的操作。另外,让我们去掉不必要的代码(新代码的灵感来自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 中,我们可以过滤我们想要查看的操作。在这种情况下,我不想显示批量删除操作的按钮:

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 来提交表单;我认为没有一种更简洁的方法来正确设置样式。

【讨论】:

以上是关于Django:如何在管理表单中的“历史记录”按钮旁边添加一个操作按钮 - 干净利落?的主要内容,如果未能解决你的问题,请参考以下文章

Django:删除管理员用户更改表单中的“现场查看”按钮

创建 Visual Studio 扩展时如何获取源代码管理历史记录窗口中的选定项目?

如何删除IE浏览器的表单历史记录?

如何在 django admin 中获取对象的历史记录?

从应用程序更改时,django 简单历史记录不起作用

如何在 python 中启用 Django shell 中的历史记录