Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to get custom Django admin actions to appear on the "change" view in addition to the "change list" view?

I thought for whatever reason this would be easy to do, but I looked deeper and it appears there is no straightforward way to allow users to execute custom admin actions on the "change" view of an instance (i.e. when you are just viewing the edit screen for a single instance, not the list of instances).

Am I overlooking an easy way to do this? Or is my only choice to override one of the admin templates (and probably the ModelAdmin.add_view method)?

like image 543
jsdalton Avatar asked May 10 '10 19:05

jsdalton


People also ask

Can we change Django admin theme?

To do so, you will have to change the project's settings.py . Find the TEMPLATES section and modify accordingly. To override the default template you first need to access the template you want to modify from the django/contrib/admin/templates/admin directory.

What is admin ModelAdmin in Django?

One of the most powerful parts of Django is the automatic admin interface. It reads metadata from your models to provide a quick, model-centric interface where trusted users can manage content on your site. The admin's recommended use is limited to an organization's internal management tool.


2 Answers

Here is update and improvement of this answer. It works with django 1.6 and redirects to where you came from.

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, 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 MyModelAdmin(ActionInChangeFormMixin, ModelAdmin):     ...... 

Template:

{% extends "admin/change_form.html" %} {% load i18n admin_static admin_list admin_urls %}  {% block extrastyle %}   {{ block.super }}   <link rel="stylesheet" type="text/css" href="{% static "admin/css/changelists.css" %}" /> {% endblock %}  {% block object-tools %}     {{ block.super }}     <div id="changelist">     <form action="{% url opts|admin_urlname:'changelist' %}" method="POST">{% csrf_token %}         {% admin_actions %}         <input type="hidden" name="_selected_action" value="{{ object_id }}">     </form>     </div> {% endblock %} 
like image 137
Jan Hosek Avatar answered Sep 18 '22 12:09

Jan Hosek


Here's what I ended up doing.

First, I extended the change_view of the ModelAdmin object as follows:

def change_view(self, request, object_id, 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     changelist_url = urlresolvers.reverse('admin:checkout_order_changelist')     return super(OrderAdmin, self).change_view(request, object_id, extra_context={         'action_form': action_form,         'changelist_url': changelist_url     }) 

Basically we're just gathering the data needed to populate the actions dropdown on the change view.

Then I just extended change_form.html for the model in question:

{% extends "admin/change_form.html" %} {% load i18n adminmedia admin_list %}  {% block extrastyle %}   {{ block.super }}   <link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/changelists.css" /> {% endblock %}  {% block object-tools %}     {{ block.super }}     <div id="changelist">     <form action="{{ changelist_url }}" method="POST">{% csrf_token %}         {% admin_actions %}         <input type="hidden" name="_selected_action" value="{{ object_id }}">     </form>     </div> {% endblock %} 

This is almost identical to how the admin actions section is outputted on the change list view. The main differences are: 1) I had to specify a URL for the form to post to, 2) instead of a checkbox to specify which object(s) should be changed, the value is set via a hidden form field, and 3) I included the CSS for the change list view, and stuck the actions in a div with id of #changelist -- just so the box would look halfway decent.

Not a great solution, but it works okay and requires no additional configuration for additional actions you might add.

like image 32
jsdalton Avatar answered Sep 21 '22 12:09

jsdalton