Skip to main content

Overview

The @action decorator enhances Django admin actions with additional features like permissions, custom icons, URL paths, and styling variants.

Decorator Signature

from unfold.decorators import action

@action(
    function: Callable | None = None,
    *,
    permissions: Iterable[str] | None = None,
    description: str | None = None,
    url_path: str | None = None,
    attrs: dict[str, Any] | None = None,
    icon: str | None = None,
    variant: ActionVariant | None = ActionVariant.DEFAULT,
) -> ActionFunction
Located in unfold/decorators.py:14

Parameters

function
Callable | None
default:"None"
The function to decorate. Can be None when using the decorator with parameters.
permissions
Iterable[str] | None
default:"None"
List of permission checks. Can be:
  • Django permission strings (e.g., "app.change_model")
  • Permission method names (e.g., "change", "delete") that will be resolved to has_<permission>_permission methods
description
str | None
default:"None"
Human-readable description displayed in the admin interface.
url_path
str | None
default:"None"
Custom URL path for the action. If not provided, the function name is used.
attrs
dict[str, Any] | None
default:"None"
HTML attributes to apply to the action button/link.
icon
str | None
default:"None"
Material Symbols icon name to display with the action.
variant
ActionVariant | None
default:"ActionVariant.DEFAULT"
Visual style variant for the action button.

ActionVariant Enum

The ActionVariant enum defines the visual styles for actions:
from unfold.enums import ActionVariant

ActionVariant.DEFAULT   # Default button style
ActionVariant.PRIMARY   # Primary button style
ActionVariant.DANGER    # Danger/warning button style
ActionVariant.SUCCESS   # Success button style

Usage Examples

Basic Action

from django.contrib import admin
from unfold.admin import ModelAdmin
from unfold.decorators import action

@admin.register(Article)
class ArticleAdmin(ModelAdmin):
    actions = ["publish_articles"]
    
    @action(description="Publish selected articles")
    def publish_articles(self, request, queryset):
        queryset.update(status="published")
        self.message_user(request, "Articles published successfully.")

Action with Icon and Variant

from unfold.decorators import action
from unfold.enums import ActionVariant

@admin.register(Article)
class ArticleAdmin(ModelAdmin):
    actions = ["archive_articles"]
    
    @action(
        description="Archive selected articles",
        icon="archive",
        variant=ActionVariant.DANGER,
    )
    def archive_articles(self, request, queryset):
        queryset.update(status="archived")

Action with Permissions

from unfold.decorators import action

@admin.register(Article)
class ArticleAdmin(ModelAdmin):
    actions = ["publish_articles"]
    
    @action(
        description="Publish selected articles",
        permissions=["change"],  # Requires change permission
        icon="publish",
    )
    def publish_articles(self, request, queryset):
        queryset.update(status="published")

Action with Django Permission String

@action(
    description="Delete all articles",
    permissions=["blog.delete_article"],  # Full permission path
    icon="delete",
    variant=ActionVariant.DANGER,
)
def delete_all(self, request, queryset):
    queryset.delete()

Detail Action

Actions can be used on detail pages:
@admin.register(Article)
class ArticleAdmin(ModelAdmin):
    actions_detail = ["send_notification"]
    
    @action(
        description="Send notification",
        url_path="notify",
        icon="notifications",
    )
    def send_notification(self, request, object_id):
        article = self.get_object(request, object_id)
        # Send notification logic
        self.message_user(request, f"Notification sent for {article}")

Row Action

Actions displayed on each row in the changelist:
@admin.register(Article)
class ArticleAdmin(ModelAdmin):
    actions_row = ["quick_publish"]
    
    @action(
        description="Quick Publish",
        icon="flash_on",
        variant=ActionVariant.SUCCESS,
    )
    def quick_publish(self, request, object_id):
        article = self.get_object(request, object_id)
        article.status = "published"
        article.save()
        return redirect(request.META.get('HTTP_REFERER'))

Custom URL Path

@action(
    description="Export to PDF",
    url_path="export-pdf",  # URL will be /admin/app/model/export-pdf/
    icon="picture_as_pdf",
)
def export_pdf(self, request, queryset):
    # Export logic
    pass

Custom HTML Attributes

@action(
    description="Dangerous Action",
    attrs={
        "data-confirm": "Are you sure?",
        "class": "custom-class",
    },
    icon="warning",
    variant=ActionVariant.DANGER,
)
def dangerous_action(self, request, queryset):
    # Action logic
    pass

Action Function Signature

The decorated function receives:
model_admin
BaseModelAdmin
required
The ModelAdmin instance.
request
HttpRequest
required
The HTTP request object.
queryset
QuerySet
The selected objects (for list actions).
object_id
str
The object ID (for detail/row actions).

Permission Handling

The decorator supports two types of permissions:
  1. Method-based permissions: Strings like "change", "delete", "view" are resolved to has_<permission>_permission(request) methods on the ModelAdmin.
  2. Django permission strings: Full paths like "app.change_model" are checked using request.user.has_perm().
All permissions must pass for the action to execute. If any permission check fails, a PermissionDenied exception is raised.

Build docs developers (and LLMs) love