Home:ALL Converter>How do I add custom actions to a change model form in Django Admin?

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

Ask Time:2016-03-10T22:21:16         Author:Chris Adams

Json Formatter

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.

I'm picturing it a bit like this at the bottom of the Django admin form for updating a single object:

bottom bar of a django admin view

The django docs explain how to add bulk actions, by adding to the actions on a model, like so:

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.

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.

Is there a fast way to do this without overriding loads of templates ?

Author:Chris Adams,eproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/35919059/how-do-i-add-custom-actions-to-a-change-model-form-in-django-admin
Antoine Pinsard :

Django Admin does not provide a way to add custom actions for change forms.\n\nHowever, you can get what you want with a few hacking.\n\nFirst you will have to override the submit row.\n\nyour_app/templates/admin/submit_line.html\n\n{% load i18n admin_urls %}\n<div class=\"submit-row\">\n{% if show_save %}<input type=\"submit\" value=\"{% trans 'Save' %}\" class=\"default\" name=\"_save\" />{% endif %}\n{% if show_delete_link %}\n {% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %}\n <p class=\"deletelink-box\"><a href=\"{% add_preserved_filters delete_url %}\" class=\"deletelink\">{% trans \"Delete\" %}</a></p>\n{% endif %}\n{% if show_save_and_copy %}<input type=\"submit\" value=\"{% trans 'Create a new item based on this one' %}\" name=\"_save_and_copy\" />{% endif %}\n{% if show_save_as_new %}<input type=\"submit\" value=\"{% trans 'Save as new' %}\" name=\"_saveasnew\" />{% endif %}\n{% if show_save_and_add_another %}<input type=\"submit\" value=\"{% trans 'Save and add another' %}\" name=\"_addanother\" />{% endif %}\n{% if show_save_and_continue %}<input type=\"submit\" value=\"{% trans 'Save and continue editing' %}\" name=\"_continue\" />{% endif %}\n</div>\n\n\nIn 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 %}. All other line are from default django implementation.\n\nThen you will have to handle your button '_save_and_copy'\n\nyour_app/admin.py\n\nfrom django.core.urlresolvers import reverse\nfrom django.http import HttpResponseRedirect\n\nclass ArticleAdmin(admin.ModelAdmin):\n\n def render_change_form(self, request, context, *args, **kwargs):\n \"\"\"We need to update the context to show the button.\"\"\"\n context.update({'show_save_and_copy': True})\n return super().render_change_form(request, context, *args, **kwargs)\n\n def response_post_save_change(self, request, obj):\n \"\"\"This method is called by `self.changeform_view()` when the form\n was submitted successfully and should return an HttpResponse.\n \"\"\"\n # Check that you clicked the button `_save_and_copy`\n if '_save_and_copy' in request.POST:\n # Create a copy of your object\n # Assuming you have a method `create_from_existing()` in your manager\n new_obj = self.model.objects.create_from_existing(obj)\n\n # Get its admin url\n opts = self.model._meta\n info = self.admin_site, opts.app_label, opts.model_name\n route = '{}:{}_{}_change'.format(*info)\n post_url = reverse(route, args=(new_obj.pk,))\n\n # And redirect\n return HttpResponseRedirect(post_url)\n else:\n # Otherwise, use default behavior\n return super().response_post_save_change(request, obj)\n\n\nThis example is for your specific case, it's up to you to make it more generic if you need to.\n\nThat 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 ?",
2016-03-10T16:12:33
yy