27

我已经为我的模型定义了一个自定义管理操作,它可以按预期完美运行。我还查看了在 SO 上向管理员更改表单页面添加按钮的多种方法。我缺少的唯一步骤是如何使更改表单页面中的按钮使用当前对象执行我的自定义管理操作?

目标是允许管理员单独检查每个对象并对它们执行操作,而无需返回列表视图、选择检查的对象并从列表中执行操作。

我的自定义管理操作如下所示:

def admin_apply_change(modeladmin, request, queryset):
    # loop over objects in query set and perform action

我假设有一种简单而干净的方法可以在管理员更改表单中调用此操作,其中queryset仅包含管理员正在查看的当前打开的对象。

注意:如果按钮位于更改表单的底部,按钮旁边,Save而不是在顶部,这将是更可取的,History这不是很明显。

解决方案

有关解决方案,请参阅Remi下面的答案。为了使其工作,需要进行以下更正:

  1. 在某些变量的初始化覆盖中response_change缺少:

    opts = self.model._meta
    pk_value = obj._get_pk_val()
    preserved_filters = self.get_preserved_filters(request)
    
  2. 新的包含标签custom_submit_row应该放在 templatetags 中,而不是放在 admin 中(见docs for custom templatetags

  3. 这是您可能会浪费一些时间的疏忽。在change_form.html您不仅要更改建议的行:

    {% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %}
    

    还有底部submit_row出现的更重要的一行:

    {% block submit_buttons_bottom %}{% submit_row %}{% endblock %}
    

    它位于 .js 中的 javascript 块的正上方change_form.html

4

4 回答 4

20

您可以查看change_form_template并将其设置为您的自定义模板并覆盖该response_change方法:

class MyModelAdmin(admin.ModelAdmin):

    # A template for a customized change view:
    change_form_template = 'path/to/your/custom_change_form.html'

    def response_change(self, request, obj):
        opts = self.model._meta
        pk_value = obj._get_pk_val()
        preserved_filters = self.get_preserved_filters(request)

        if "_customaction" in request.POST:
            # handle the action on your obj
            redirect_url = reverse('admin:%s_%s_change' %
                               (opts.app_label, opts.model_name),
                               args=(pk_value,),
                               current_app=self.admin_site.name)
             redirect_url = add_preserved_filters({'preserved_filters': preserved_filters, 'opts': opts}, redirect_url)
             return HttpResponseRedirect(redirect_url)
        else:
             return super(MyModelAdmin, self).response_change(request, obj)

change_form.html从您的复制site-packages/django/contrib/admin/templates/change_form.html并编辑第 40 行

 {% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %}

 {% if save_on_top %}{% block submit_buttons_top %}{% custom_submit_row %}{% endblock %}{% endif %}

还要检查该行:

 {% block submit_buttons_bottom %}{% submit_row %}{% endblock %}

就在 javascript 块的上方。

然后,您可以在 admin.py 中的某处注册一个新的包含标签或将其添加到模板标签中:

@register.inclusion_tag('path/to/your/custom_submit_line.html', takes_context=True)
def custom_submit_row(context):
    """
    Displays the row of buttons for delete and save.
    """
    opts = context['opts']
    change = context['change']
    is_popup = context['is_popup']
    save_as = context['save_as']
    ctx = {
        'opts': opts,
        'show_delete_link': (
            not is_popup and context['has_delete_permission'] and
            change and context.get('show_delete', True)
        ),
        'show_save_as_new': not is_popup and change and save_as,
        'show_save_and_add_another': (
            context['has_add_permission'] and not is_popup and
            (not save_as or context['add'])
        ),
        'show_save_and_continue': not is_popup and context['has_change_permission'],
        'is_popup': is_popup,
        'show_save': True,
        'preserved_filters': context.get('preserved_filters'),
    }
    if context.get('original') is not None:
        ctx['original'] = context['original']
    return ctx

你的内容custom_submit_line.html

{% load i18n admin_urls %}
<div class="submit-row">
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save" />{% endif %}
{% if show_delete_link %}
    {% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %}
    <p class="deletelink-box"><a href="{% add_preserved_filters delete_url %}" class="deletelink">{% trans "Delete" %}</a></p>
{% endif %}
{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" />{% endif %}
{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" />{% endif %}
{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" />{% endif %}

<input type="submit" value="{% trans 'Custom Action' %}"  name="_customaction" />

</div>

这是很多代码,但主要是复制/粘贴。希望有帮助。

于 2016-01-20T12:17:26.347 回答
12

大多数人可能会不假思索地这样做,尽管从答案中不清楚管理员更改表单应该简单地扩展而不是完全覆盖。

custom_change_form.html

{% extends "admin/change_form.html" %}

{% if save_on_top %}{% block submit_buttons_top %}{% custom_submit_row %}{% endblock %}{% endif %}

{% block submit_buttons_bottom %}{% custom_submit_row %}{% endblock %}
于 2016-12-30T00:03:04.437 回答
2

或者,您可以扩展submit_line.html文件,添加您的自定义按钮(在更改页面的顶部和底部)。

您的文件templates/adminyour_app_name/your_model_name.html将以:

{% extends "admin/submit_line.html" %}
{% load i18n admin_urls %}
<div class="submit-row">
  {% block submit-row %}  
     ... YOUR BUTTONS HERE ...
  {% endblock %}
</div>
于 2019-07-31T17:19:39.137 回答
1

根据 Remi 的回答,一个更简洁的模板解决方案可以submit-row在模板中覆盖submit_line.html

{% extends "admin/submit_line.html" %}
{% load i18n admin_urls %}
<div class="submit-row">
{% block submit-row %}
{{ block.super }}

{% if custom_buttons_template %}{% include custom_buttons_template %}{% endif %}

{% endblock %}

change_view您可以添加到上下文中custom_buttons_template,以便您可以分别自定义每个模型上的自定义按钮。

def change_view(self, request, object_id, form_url='', extra_context=None):
    extra_context = extra_context or {}
    extra_context['custom_buttons_template'] = 'admin/test.html'
    return super(TransactionFileAdmin, self).change_view(
        request, object_id, form_url, extra_context=extra_context,
    )

在此之后,我的模板将加载admin/test.html并将内容直接插入到Save按钮之前的提交行中。如果您想将按钮放在其他地方,您可以完整复制submit_line.html并放在custom_buttons_template任何地方。

于 2021-10-07T20:40:41.280 回答