Defining Admin Actions
Admin actions allow you to perform bulk operations on multiple objects from the changelist view or specific operations on a single object from the change form.
Creating a Basic Action
To create an action, define a function that accepts a ModelAdmin, an HttpRequest, and a QuerySet of selected objects. Then, register it in the actions attribute of your ModelAdmin.
from django.contrib import admin
from myapp.models import Article
@admin.action(description="Mark selected stories as published")
def make_published(modeladmin, request, queryset):
queryset.update(status="p")
class ArticleAdmin(admin.ModelAdmin):
list_display = ["title", "status"]
actions = [make_published]
admin.site.register(Article, ArticleAdmin)
Configuring Action Metadata
Use the @admin.action decorator to define the action's display name and required permissions.
from django.contrib import admin
class ArticleAdmin(admin.ModelAdmin):
actions = ["make_published"]
@admin.action(
description="Mark selected stories as published",
permissions=["change"],
)
def make_published(self, request, queryset):
queryset.update(status="p")
Action Locations
By default, actions appear in the dropdown on the changelist page. You can use ActionLocation to make an action available on the change form (the individual object edit page) as well.
from django.contrib import admin
from django.contrib.admin.options import ActionLocation
@admin.action(
description="Send external mail",
location=(ActionLocation.CHANGE_LIST, ActionLocation.CHANGE_FORM),
)
def send_mail_action(modeladmin, request, queryset):
# Implementation for sending mail
pass
class SubscriberAdmin(admin.ModelAdmin):
actions = [send_mail_action]
[!NOTE] Actions on the
CHANGE_FORMare only available for existing objects, not when adding a new object.
Customizing the Action Bar
You can customize the action bar (e.g., adding JavaScript or CSS) by subclassing ActionForm and assigning it to action_form on your ModelAdmin.
from django.contrib import admin
from django.contrib.admin.helpers import ActionForm
class MediaActionForm(ActionForm):
class Media:
js = ["path/to/custom_action.js"]
css = {"all": ["path/to/custom_action.css"]}
class ArticleAdmin(admin.ModelAdmin):
action_form = MediaActionForm
actions = ["custom_action"]
@admin.action(description="Run custom JS action")
def custom_action(self, request, queryset):
pass
Providing Actions Dynamically
Override get_actions() to conditionally enable or disable actions based on the request or the specific ModelAdmin instance.
from django.contrib.admin.options import ActionLocation
class ArticleAdmin(admin.ModelAdmin):
actions = ["delete_selected"]
def get_actions(self, request, action_location=ActionLocation.CHANGE_LIST):
actions = super().get_actions(request, action_location=action_location)
if not request.user.is_superuser:
# Remove 'delete_selected' for non-superusers
if "delete_selected" in actions:
del actions["delete_selected"]
return actions
Troubleshooting and Best Practices
Deprecation of Tuple Unpacking
In older versions of Django, actions were represented as tuples. In this codebase, actions are instances of the Action class. While they still support tuple-like indexing and unpacking for backward compatibility, you should access attributes directly.
Avoid:
# Deprecated: Unpacking an action tuple
func, name, desc = actions["my_action"]
Recommended:
# Use Action attributes
action = actions["my_action"]
func = action.func
name = action.name
desc = action.description
Signature Requirements
When overriding get_actions() or get_action_choices(), always include the action_location parameter. Overriding these without it is deprecated and will trigger a RemovedInDjango70Warning.
# Correct signature
def get_actions(self, request, action_location=ActionLocation.CHANGE_LIST):
...