The modeladmin
A HTTP request
A queryset of the selected models
An example of a Ordermodel with a custom action to update status for all selected items may look like this:
Python# admin.py
class OrderAdmin(admin.ModelAdmin):
actions = ['update_status']
def update_status(self, request, queryset):
queryset.update(status='NEW_STATUS')
update_status.short_description = "Update status"
The short_description
property is used for customizing the label in the action dropdown box.
Adding an intermediate page
To add an intermediate page you will continue from the code above, but instead of executing the update right away you will treat it like a view and return a HTTP response including a new template and context.
To begin with just return a template with an empty context:
Python# admin.py
from django.shortcuts import render
class OrderAdmin(admin.ModelAdmin):
actions = ['update_status']
def update_status(self, request, queryset):
return render(request,
'admin/order_intermediate.html',
context={})
update_status.short_description = "Update status"
To keep the admin look & feel extend from djangos own admin base template:
Python# 'admin/order_intermediate.html'
{% extends "admin/base_site.html" %}
{% block content %}
Are you sure you want to execute this action ?
{% endblock %}
Try the above code and you will land on an intermediate page asking you if you want to perform the selected action, however you wont have to option to actually do anything. Let's fix that.
Adding a form to execute our action
First of lets add a simple form to our template. We could use a django form from our context but for now lets code everything by hand. There are three important form properties that you have to include:
An
action
key, which should map to your custom admin actionAn
apply
key, which can be anything really. This is to catch our submission later on in the viewThe selected items, in the form of
_selected_action
which should be each of the selected itemspks
.
Our updated template now looks like this:
Python# 'admin/order_intermediate.html'
{% extends "admin/base_site.html" %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<p>
Are you sure you want to execute this action on the selected items?
</p>
{% for order in orders %}
<p>
{{ order }}
</p>
<input type="hidden" name="_selected_action" value="{{ order.pk }}" />
{% endfor %}
<input type="hidden" name="action" value="update_status" />
<input type="submit" name="apply" value="Update status"/>
</form>
{% endblock %}
Note how we added hidden fields for the _selected_action
values as well as the action
property.
Now to catch this form submit in our admin action method:
Python# admin.py
from django.shortcuts import render
from django.http import HttpResponseRedirect
class OrderAdmin(admin.ModelAdmin):
actions = ['update_status']
def update_status(self, request, queryset):
# All requests here will actually be of type POST
# so we will need to check for our special key 'apply'
# rather than the actual request type
if 'apply' in request.POST:
# The user clicked submit on the intermediate form.
# Perform our update action:
queryset.update(status='NEW_STATUS')
# Redirect to our admin view after our update has
# completed with a nice little info message saying
# our models have been updated:
self.message_user(request,
"Changed status on {} orders".format(queryset.count()))
return HttpResponseRedirect(request.get_full_path())
return render(request,
'admin/order_intermediate.html',
context={'orders':queryset})
update_status.short_description = "Update status"
Thats it!