在我的Tournament
管理员中,我想添加一个用于创建匹配结构的按钮,该按钮取决于之前注册的竞争Team
。这是一个涉及单个锦标赛的动作,并且没有"结果" - 它只是对数据库进行了一次中等复杂的更新。
我将功能实现为admin action,它运行良好,似乎是实现该功能的最佳方式。但是,大多数情况下,用户都希望从锦标赛页面而不是锦标赛列表中执行该操作。
This回答显示了如何将管理操作表单添加到相关页面,但我不需要下拉列表,其中还包括" delete"行动;一个简单的按钮会更好。
This问题是关于添加按钮(虽然它是列表页面的按钮),但正确的样式仅适用于a href
标签,而不适用于按钮;我正在执行某个操作,而不是显示新文档。我想避免重复相关的CSS来使用表单提交按钮。我还想避免硬编码操作名称,或者通常重复管理操作下拉列表会为我推断的内容。
所以具体问题是:
a
标记并通过JavaScript提交,还是有更好的方式?{% admin_actions %}
(生成带有下拉列表的表单)最好地复制this回答的逻辑?也就是说,检索管理员操作,使用他们的描述等显示它们,但作为按钮?相关代码如下:
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 %}
答案 0 :(得分: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
使用元组填充<select>
以用作选项。
为了尽可能灵活,我将介绍一种新方法,该方法仅检索我们想要显示的操作。另外,让我们摆脱不必要的代码(新代码的灵感来自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提交表单;我认为没有更清晰的方式来塑造造型。