Django Admin is awesome but it lacks an important feature: Editing multiple items in batch. There are two django add-ons this functionality but both seem a bit out of date.
Here I would like to show an example of how simple batch editing can be added to django admin with minimal amount of code, taking advantage of django admin’s own get_form()
.
# admin.py
def batch_update_view(model_admin, request, queryset, field_name):
# removes all other fields from the django admin form for a model
def remove_fields(form):
for field in list(form.base_fields.keys()):
if not field == field_name:
del form.base_fields[field]
return form
# the return value is the form class, not the form class instance
form_class = remove_fields(model_admin.get_form(request))
if request.method == 'POST':
form = form_class()
# the view is already called via POST from the django admin changelist
# here we have to distinguish between just showing the intermediary view via post
# and actually confirming the bulk edits
# for this there is a hidden field 'form-post' in the html template
if 'form-post' in request.POST:
form = form_class(request.POST)
if form.is_valid():
for item in queryset.all():
setattr(item, field_name, form.cleaned_data[field_name])
item.save()
model_admin.message_user(request, "Changed offer on {} items".format(queryset.count()))
return HttpResponseRedirect(request.get_full_path())
return render(
request,
'admin/batch_editing_intermediary.html',
context={
'form': form,
'items': queryset,
'media': model_admin.media,
}
)
The template for the intermediary view could be made model agnostic by passing a list of (id, string) tuples to the template instead of item objects:
templates/admin/batch_editing_intermediary.html
{% extends "admin/base_site.html" %}
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
{{ media }}
{% endblock %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<p>
{{ form }}
</p>
<ul>
{% for item in items %}
<li>
{{ item.id }} - {{ item.date }} - {{ item.who }} - {{ item.offer }} - {{ item.offer_work_type }}
</li>
{# this is required by Django Admin custom action framework #}
<input type="hidden" name="_selected_action" value="{{ item.pk }}"/>
{% endfor %}
</ul>
{# this is required by Django Admin custom action framework #}
<input type="hidden" name="action" value="{{ request.POST.action }}"/>
<input type="hidden" name="form-post" value="True"/>
<input type="submit" name="apply" value="Save"/>
</form>
{% endblock %}
class YourModelAdmin(admin.ModelAdmin):
actions = [
'custom_batch_editing_field1_admin_action',
'custom_batch_editing_field2_admin_action',
]
def custom_batch_editing_field1_admin_action(self, request, queryset):
return batch_update_view(
model_admin=self,
request=request,
queryset=queryset,
# this is the name of the field on the YourModel model
field_name='field1',
)
batch_update_offer.short_description = "batch update field1"