如何在Django Admin中将自定义操作添加到更改模型表单?

时间:2016-03-10 14:21:17

标签: python django django-admin

我正在与Django管理员合作,我希望能够使用模型的现有实例作为制作新对象的模板,因此使用django管理员的人不会在制作新对象时,需要重新键入对象上的所有相同属性。

我在Django管理表单底部想象它有点像这样更新单个对象:

bottom bar of a django admin view

django文档解释了如何通过添加模型上的操作来添加批量操作,如下所示:

class ArticleAdmin(admin.ModelAdmin):

    actions = ['make_published']

    def make_published(self, request, queryset):
        queryset.update(status='p')

    make_published.short_description = "Mark selected stories as published"

但是,对于我来说,如何对一个对象上的单个change model表单执行此操作并不是很清楚,对于我一次只想应用于模型的操作。

我该怎么做?

我猜我可能需要用change_model form进行攻击,但除​​此之外,我不太确定。

有没有一种快速的方法可以在不覆盖大量模板的情况下执行此操作?

2 个答案:

答案 0 :(得分:5)

Django Admin没有提供为更改表单添加自定义操作的方法。

然而,你可以通过一些黑客来获得你想要的东西。

首先,您必须覆盖提交行。

<强> your_app /模板/管理/ 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_and_copy %}<input type="submit" value="{% trans 'Create a new item based on this one' %}" name="_save_and_copy" />{% 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 %}
</div>

在上面的模板中,我刚添加了行{% if show_save_and_copy %}<input type="submit" value="{% trans 'Create a new item based on this one' %}" name="_save_and_copy" />{% endif %}。所有其他行都是from default django implementation

然后你必须处理你的按钮'_save_and_copy'

<强> your_app / admin.py

from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect

class ArticleAdmin(admin.ModelAdmin):

    def render_change_form(self, request, context, *args, **kwargs):
        """We need to update the context to show the button."""
        context.update({'show_save_and_copy': True})
        return super().render_change_form(request, context, *args, **kwargs)

    def response_post_save_change(self, request, obj):
        """This method is called by `self.changeform_view()` when the form
        was submitted successfully and should return an HttpResponse.
        """
        # Check that you clicked the button `_save_and_copy`
        if '_save_and_copy' in request.POST:
            # Create a copy of your object
            # Assuming you have a method `create_from_existing()` in your manager
            new_obj = self.model.objects.create_from_existing(obj)

            # Get its admin url
            opts = self.model._meta
            info = self.admin_site, opts.app_label, opts.model_name
            route = '{}:{}_{}_change'.format(*info)
            post_url = reverse(route, args=(new_obj.pk,))

            # And redirect
            return HttpResponseRedirect(post_url)
        else:
            # Otherwise, use default behavior
            return super().response_post_save_change(request, obj)

此示例适用于您的具体情况,如果您需要,可以使其更通用。

话虽如此,对于您的具体情况,您也可以单击“保存并继续”以保存您的工作,然后单击“另存为新”以复制它。不是吗?

答案 1 :(得分:2)

如前所述,没有办法,需要被黑客入侵。我认为这是一个优雅的技巧,可以在列表和更改表单视图中添加自定义操作。它实际上并不保存表单,而只是对当前对象执行任何所需的自定义操作,然后将您返回到相同的更改表单页面。

from django.db.models import Model

from django.contrib import admin, messages
from django.contrib.admin.options import (
    unquote,
    csrf_protect_m,
    HttpResponseRedirect,
)


class ArticleAdmin(admin.ModelAdmin):
    change_form_template = 'book/admin_change_form_book.html'

    actions = ['make_published']

    def make_published(self, request, queryset):
        if isinstance(queryset, Model):
            obj = queryset
            obj.status = 'p'
            obj.save()
            updated_count = 1
        else:
            updated_count = queryset.update(status='p')

        msg = "Marked {} new objects from existing".format(updated_count)
        self.message_user(request, msg, messages.SUCCESS)

    make_published.short_description = "Mark selected stories as published"

    @csrf_protect_m
    def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
        if request.method == 'POST' and '_make_published' in request.POST:
            obj = self.get_object(request, unquote(object_id))
            self.make_published(request, obj)
            return HttpResponseRedirect(request.get_full_path())

        return admin.ModelAdmin.changeform_view(
            self, request,
            object_id=object_id,
            form_url=form_url,
            extra_context=extra_context,
        )

现在,您可以将操作的<input>添加到自定义模板视图中(此示例在change_form_template中使用book/admin_change_form_book.html

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

{% block form_top %}
<input
    type="submit"
    name="_make_published"            
    value="Mark Published"
    class="grp-button grp-default"
>
{% endblock %}

如果您查看admin/change_form.html(即“ django / contrib / admin / templates / admin / change_form.html”),则可以在<input>之间的任意位置插入此自定义<form...> </form>操作页面上的标签。包括以下这些块:

  • {% block form_top %}
  • {% block after_related_objects %}
  • {% block submit_buttons_bottom %}