Skip to main content

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_FORM are 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):
...