简体   繁体   English

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

[英]How do I add custom actions to a change model form in Django Admin?

I'm working with Django admin, and I'd like to be able to use an existing instance of a model as a template for making a new object, so people using django admin don't need to re-key all the same properties on a object, when making new objects.我正在使用 Django 管理员,我希望能够使用模型的现有实例作为创建新对象的模板,因此使用 django 管理员的人不需要重新键入所有相同的属性在创建新对象时,在对象上。

I'm picturing it a bit like this at the bottom of the Django admin form for updating a single object:我在用于更新单个对象的 Django 管理表单的底部将其描绘得有点像这样:

django 管理视图的底栏

The django docs explain how to add bulk actions, by adding to the actions on a model, like so: 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"

However, it wasn't so clear to me how to do this for a single change model form on an object, for actions I only want to apply to model at a time.但是,我并不清楚如何为对象上的单个change model表单执行此操作,对于我只想一次应用于模型的操作。

How would I do this?我该怎么做?

I'm guessing I probably need to hack around with the change_model form , but beyond that, I'm not so sure.我猜我可能需要修改 change_model 表单,但除此之外,我不太确定。

Is there a fast way to do this without overriding loads of templates ?有没有一种快速的方法可以在不覆盖大量模板的情况下做到这一点?

Django Admin does not provide a way to add custom actions for change forms. Django Admin 不提供为更改表单添加自定义操作的方法。

However, you can get what you want with a few hacking.但是,您可以通过一些黑客行为获得您想要的东西。

First you will have to override the submit row.首先,您必须覆盖提交行。

your_app/templates/admin/submit_line.html your_app/templates/admin/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>

In the above template, I just added the line {% 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_and_copy %}<input type="submit" value="{% trans 'Create a new item based on this one' %}" name="_save_and_copy" />{% endif %} All other line are from default django implementation .所有其他行都来自默认的 django implementation

Then you will have to handle your button '_save_and_copy'然后你将不得不处理你的按钮'_save_and_copy'

your_app/admin.py 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)

This example is for your specific case, it's up to you to make it more generic if you need to.此示例适用于您的特定情况,如果需要,您可以使其更通用。

That being said, for your specific case you can also just click "Save and continue" to save your work, and then click "Save as new" to make a copy of it.话虽如此,对于您的具体情况,您也可以单击“保存并继续”以保存您的工作,然后单击“另存为新”以制作副本。 Don't you ?不是吗?

As pointed out, there is not a way and needs to be hacked.正如所指出的,没有办法并且需要被黑客入侵。 Here's I think an elegant hack for adding custom actions to both the list and change form views.这是我认为向列表和更改表单视图添加自定义操作的优雅技巧。 It doesn't actually save the form just execute whatever custom action you want against the current object and return you back to the same change form page.它实际上并没有保存表单,只是对当前对象执行您想要的任何自定义操作并将您返回到相同的更改表单页面。

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,
        )

Now you can add an <input> for the action to the custom template view (this example uses book/admin_change_form_book.html in change_form_template)现在您可以为自定义模板视图添加一个<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 %}

If you look at the admin/change_form.html (ie "django/contrib/admin/templates/admin/change_form.html") you can insert this custom <input> action anywhere between the <form...> </form> tags on the page.如果您查看admin/change_form.html (即“django/contrib/admin/templates/admin/change_form.html”),您可以在<form...> </form>之间的任意位置插入此自定义<input>操作页面上的标签。 Including these blocks:包括这些块:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM