Skip to content

Actions API Reference

Actions represent operations that can be performed on models in django-admin-deux. They provide a flexible, composable system for creating custom functionality through mixin composition.

Action Architecture

Actions are built using a mixin composition pattern:

  1. Action Type Mixin - Defines where/how the action is triggered (List, Bulk, or Record)
  2. View Type Mixin - Defines which Django CBV to use (ListView, FormView, CreateView, etc.)
  3. BaseAction - Provides core action functionality
class MyAction(ActionTypeMixin, ViewTypeMixin, BaseAction):
    label = 'My Action'
    # ... configuration

BaseAction

The foundation class for all actions.

from djadmin.actions import BaseAction

class BaseAction:
    """Base class for all actions."""

Constructor

def __init__(self, model, model_admin, admin_site)

Parameters

  • model (type): The Django model class this action operates on
  • model_admin (ModelAdmin): The ModelAdmin instance
  • admin_site (AdminSite): The AdminSite instance

Raises

  • ValueError: If label is not defined on the action class

Note: Actions are typically instantiated automatically by ModelAdmin. You rarely need to instantiate them directly.

Core Attributes

label

label: str = None  # Required

Display name for the action (shown in buttons, menus, etc.).

Type: str (required)

Example:

class ExportAction(BaseAction):
    label = 'Export to CSV'

icon

icon: str = None

Icon name or CSS class for the action button.

Type: str or None Default: None

Example:

class DeleteAction(BaseAction):
    label = 'Delete'
    icon = 'trash'  # Icon identifier (theme-dependent)

css_class

css_class: str = ''

Additional CSS classes for the action button.

Type: str Default: ''

Example:

class DeleteAction(BaseAction):
    label = 'Delete'
    css_class = 'danger'  # Red/destructive styling

class PrimaryAction(BaseAction):
    label = 'Save'
    css_class = 'primary'  # Primary/prominent styling

confirmation_required

confirmation_required: bool = False

Whether to show confirmation before executing the action.

Type: bool Default: False

Example:

class DeleteAction(BaseAction):
    label = 'Delete'
    confirmation_required = True  # Shows confirmation page

http_method

http_method: str = 'GET'

HTTP method for triggering this action.

Type: str ('GET' or 'POST') Default: 'GET'

Example:

class CreateAction(BaseAction):
    label = 'Create'
    http_method = 'POST'  # Form submission

Permission Control (New in Milestone 5)

permission_class

permission_class: BasePermission | None = None

Declarative permission control using the new permission system (Milestone 5).

Type: BasePermission instance or None Default: Inherits from ModelAdmin.permission_class

Available permission classes: - AllowAny - No restrictions - IsAuthenticated - Requires authenticated user - IsStaff - Requires staff status - IsSuperuser - Requires superuser status - HasDjangoPermission() - Checks Django model permissions

Permission composition:

from djadmin.plugins.permissions import IsStaff, HasDjangoPermission

class DeleteAction(BaseAction):
    label = 'Delete'
    # Both conditions must be met
    permission_class = IsStaff() & HasDjangoPermission()

Example with override:

@register(Product)
class ProductAdmin(ModelAdmin):
    # Default permission for all actions
    permission_class = IsStaff()

    # Override for specific action
    list_actions = [
        AddAction(permission_class=IsSuperuser()),  # Only superusers can add
    ]

django_permission_name

django_permission_name: str = 'view'

The Django permission name used by HasDjangoPermission() to construct the full permission string.

Type: str Default: 'view' Common values: 'add', 'change', 'delete', 'view'

Example:

from djadmin.plugins.core.actions import AddAction

class AddAction(BaseAction):
    label = 'Add'
    django_permission_name = 'add'  # Used to check 'app_label.add_model' permission

Note: The HasDjangoPermission() class automatically constructs the full permission string from this attribute: - Model: Product (app: webshop) - Action: AddAction with django_permission_name = 'add' - Checks: webshop.add_product

Permission Methods

check_permission()

def check_permission(self, request: HttpRequest) -> bool

Check if user has permission to execute this action at runtime.

Parameters: - request (HttpRequest): The HTTP request

Returns: bool - True if user has permission, False otherwise

How it works: 1. Creates a temporary view instance with PermissionMixin 2. Calls view.test_func() which evaluates the permission_class 3. Returns the result

Usage:

# Automatically called by ModelAdmin.filter_actions()
action = DeleteBulkAction(model, model_admin, site)
if action.check_permission(request):
    # User can execute this action
    filtered_actions.append(action)

Note: This method is called automatically by the framework. You typically don't need to call it directly.

URL Methods

url_name

@property
def url_name(self) -> str

Get URL name for this action.

Returns: str - URL name (e.g., 'webshop_product_export')

Example:

action = ExportAction(Product, product_admin, site)
print(action.url_name)  # 'webshop_product_export'

# Use with reverse
from django.urls import reverse
url = reverse(f'admin:{action.url_name}')

action_name (property)

@property
def action_name(self) -> str

Get the normalized action name derived from the class name.

Returns: str - Normalized action name (e.g., 'edit', 'delete_bulk')

How it works: - Uses slugify(ClassName) to convert class name to lowercase with hyphens - Replaces hyphens with underscores - Removes the 'action' suffix - Example: EditAction → 'edit', DeleteBulkAction → 'delete_bulk'

Example:

class ExportAction(BaseAction):
    label = 'Export'

action = ExportAction(Product, product_admin, site)
print(action.action_name)  # 'export'

get_url_pattern()

def get_url_pattern(self) -> str

Get URL pattern for this action.

Returns: str - URL pattern without leading slash

Default Pattern: '{app_label}/{model_name}/{action_name}/'

Note: The default pattern now uses action_name property instead of hardcoding class names. For RecordActionMixin actions, the pattern includes <int:pk>/{action_name}/.

Override to customize the URL pattern:

class AddAction(BaseAction):
    label = 'Add'

    def get_url_pattern(self):
        opts = self.model._meta
        return f'{opts.app_label}/{opts.model_name}/__new__/'  # Custom pattern

View Methods

get_base_class()

def get_base_class(self) -> type

Get the Django CBV base class for this action.

Returns: type - View class (e.g., FormView, CreateView)

How it works: - Looks through the action's MRO for view-type mixins - Returns the base_class from the first view-type mixin found - Falls back to TemplateView if no view-type mixin is present

Example:

class MyAction(FormViewActionMixin, BaseAction):
    label = 'My Action'

action = MyAction(model, admin, site)
print(action.get_base_class())  # <class 'django.views.generic.FormView'>

get_template_names()

def get_template_names(self) -> list[str]

Get template names as a list (Django CBV compatibility).

Returns: list[str] - List of template paths

Default Implementation: Automatically generates template paths using action_name: 1. Model-specific: 'djadmin/{app_label}/{model_name}_{action_name}.html' 2. Generic fallback: 'djadmin/actions/{action_name}.html'

Example (automatic behavior):

class EditAction(RecordActionMixin, UpdateViewActionMixin, BaseAction):
    label = 'Edit'
    # No need to define get_template_names() - automatic!

# Generates:
# ['djadmin/webshop/product_edit.html', 'djadmin/actions/edit.html']

Override only when you need custom template paths:

class MyAction(BaseAction):
    label = 'My Action'

    def get_template_names(self):
        opts = self.model._meta
        return [
            f'djadmin/{opts.app_label}/{opts.model_name}_custom.html',
            'djadmin/actions/custom_template.html',
        ]

Action Type Mixins

These mixins define where and how an action is triggered.

GeneralActionMixin

from djadmin.actions import GeneralActionMixin

For actions that operate on the list view without requiring record selection.

Attributes: - action_type = 'list'

Method to implement:

def execute(self, request: HttpRequest, **kwargs) -> HttpResponse

Parameters: - request (HttpRequest): The HTTP request - kwargs: Additional context

Returns: HttpResponse

Use cases: - Add new record - Import records - Export all records - Refresh/reload list

Example:

from djadmin.actions import BaseAction, GeneralActionMixin
from djadmin.actions.view_mixins import TemplateViewActionMixin

class ImportAction(GeneralActionMixin, TemplateViewActionMixin, BaseAction):
    label = 'Import'
    icon = 'upload'

    def get_template_name(self):
        return 'myapp/import.html'

BulkActionMixin

from djadmin.actions import BulkActionMixin

For actions that operate on multiple selected records via checkboxes.

Attributes: - action_type = 'bulk'

Method to implement:

def execute(self, request: HttpRequest, queryset, **kwargs) -> HttpResponse

Parameters: - request (HttpRequest): The HTTP request - queryset (QuerySet): QuerySet of selected records - kwargs: Additional context

Returns: HttpResponse

Use cases: - Delete selected records - Bulk update status - Export selected records - Bulk assign to category

Example:

from djadmin.actions import BaseAction, BulkActionMixin
from djadmin.actions.view_mixins import BulkDeleteViewActionMixin

class DeleteBulkAction(BulkActionMixin, BulkDeleteViewActionMixin, BaseAction):
    label = 'Delete Selected'
    icon = 'trash'
    confirmation_required = True

    def get_template_name(self):
        return 'djadmin/actions/delete_bulk.html'

RecordActionMixin

from djadmin.actions import RecordActionMixin

For actions that operate on a single record.

Attributes: - action_type = 'record'

Method to implement:

def execute(self, request: HttpRequest, obj, **kwargs) -> HttpResponse

Parameters: - request (HttpRequest): The HTTP request - obj: The model instance - kwargs: Additional context

Returns: HttpResponse

Use cases: - Edit record - Delete record - Duplicate/clone record - View record details - Download record as PDF

Example:

from djadmin.actions import BaseAction, RecordActionMixin
from djadmin.actions.view_mixins import UpdateViewActionMixin

class EditAction(RecordActionMixin, UpdateViewActionMixin, BaseAction):
    label = 'Edit'
    icon = 'pencil'

    def get_template_name(self):
        return 'djadmin/actions/edit.html'

View Type Mixins

These mixins define which Django CBV the action should use.

FormViewActionMixin

from djadmin.actions.view_mixins import FormViewActionMixin

Use Django's FormView for actions with non-model forms.

Attributes: - base_class = FormView

Use for: Actions with plain Form (not ModelForm)

Example:

from django import forms
from djadmin.actions import BaseAction, BulkActionMixin
from djadmin.actions.view_mixins import FormViewActionMixin

class StatusForm(forms.Form):
    status = forms.ChoiceField(choices=[('active', 'Active'), ('inactive', 'Inactive')])

class ChangeStatusAction(BulkActionMixin, FormViewActionMixin, BaseAction):
    label = 'Change Status'

    def get_form_class(self):
        return StatusForm

    def get_template_name(self):
        return 'myapp/change_status.html'

CreateViewActionMixin

from djadmin.actions.view_mixins import CreateViewActionMixin

Use Django's CreateView for creating model instances.

Attributes: - base_class = CreateView

Use for: Creating new records with ModelForm

Example:

from djadmin.actions import BaseAction, GeneralActionMixin
from djadmin.actions.view_mixins import CreateViewActionMixin

class AddAction(GeneralActionMixin, CreateViewActionMixin, BaseAction):
    label = 'Add'
    icon = 'plus'

    def get_template_name(self):
        return 'djadmin/actions/add.html'

    def get_form_class(self):
        return self.model_admin.create_form_class or auto_generate_form()

UpdateViewActionMixin

from djadmin.actions.view_mixins import UpdateViewActionMixin

Use Django's UpdateView for updating model instances.

Attributes: - base_class = UpdateView

Use for: Editing existing records with ModelForm

Example:

from djadmin.actions import BaseAction, RecordActionMixin
from djadmin.actions.view_mixins import UpdateViewActionMixin

class EditAction(RecordActionMixin, UpdateViewActionMixin, BaseAction):
    label = 'Edit'
    icon = 'pencil'

    def get_template_name(self):
        return 'djadmin/actions/edit.html'

    def get_form_class(self):
        return self.model_admin.update_form_class or auto_generate_form()

ListActionMixin

from djadmin.actions.view_mixins import ListActionMixin

Use Django's ListView for displaying lists of objects.

Attributes: - base_class = ListView

Use for: Main list view for a model

Example:

from djadmin.actions import BaseAction
from djadmin.actions.view_mixins import ListActionMixin

class ListAction(ListActionMixin, BaseAction):
    label = 'List'

    def get_template_name(self):
        opts = self.model._meta
        return f'djadmin/{opts.app_label}/{opts.model_name}_list.html'

    def get_queryset(self):
        # Custom queryset logic
        return self.model.objects.filter(is_active=True)

TemplateViewActionMixin

from djadmin.actions.view_mixins import TemplateViewActionMixin

Use Django's TemplateView for simple template rendering.

Attributes: - base_class = TemplateView

Use for: Actions that render templates without object/queryset

Example:

from djadmin.actions import BaseAction, GeneralActionMixin
from djadmin.actions.view_mixins import TemplateViewActionMixin

class HelpAction(GeneralActionMixin, TemplateViewActionMixin, BaseAction):
    label = 'Help'
    icon = 'question'

    def get_template_name(self):
        return 'djadmin/help.html'

DeleteViewActionMixin

from djadmin.actions.view_mixins import DeleteViewActionMixin

Use Django's DeleteView for deleting single records with confirmation.

Attributes: - base_class = DeleteView

Use for: Deleting single records

Example:

from djadmin.actions import BaseAction, RecordActionMixin
from djadmin.actions.view_mixins import DeleteViewActionMixin

class DeleteAction(RecordActionMixin, DeleteViewActionMixin, BaseAction):
    label = 'Delete'
    icon = 'trash'
    css_class = 'danger'
    confirmation_required = True

    def get_template_name(self):
        return 'djadmin/actions/delete.html'

    def get_success_url(self):
        # Redirect to list after deletion
        return reverse(f'admin:{self.model_admin.list_url_name}')

BulkDeleteViewActionMixin

from djadmin.actions.view_mixins import BulkDeleteViewActionMixin

Use custom BulkDeleteView for deleting multiple records with confirmation.

Attributes: - base_class = BulkDeleteView

Use for: Deleting multiple selected records

Example:

from djadmin.actions import BaseAction, BulkActionMixin
from djadmin.actions.view_mixins import BulkDeleteViewActionMixin

class DeleteBulkAction(BulkActionMixin, BulkDeleteViewActionMixin, BaseAction):
    label = 'Delete Selected'
    icon = 'trash'
    confirmation_required = True

    def get_template_name(self):
        return 'djadmin/actions/delete_bulk.html'

    def get_success_url(self):
        return reverse(f'admin:{self.model_admin.list_url_name}')

RedirectViewActionMixin

from djadmin.actions.view_mixins import RedirectViewActionMixin

Use Django's RedirectView for redirecting to another URL.

Attributes: - base_class = RedirectView

Use for: Actions that redirect without displaying a view

Example:

from djadmin.actions import BaseAction, RecordActionMixin
from djadmin.actions.view_mixins import RedirectViewActionMixin

class ViewAction(RecordActionMixin, RedirectViewActionMixin, BaseAction):
    label = 'View Details'

    def get_redirect_url(self, request, obj):
        return obj.get_absolute_url()

Action Composition Pattern

Actions are composed using multiple inheritance:

class MyAction(
    ActionTypeMixin,    # Where: List | Bulk | Record
    ViewTypeMixin,      # How: FormView | CreateView | ListView | etc.
    BaseAction,         # Core functionality
):
    label = 'My Action'
    # ... configuration

Built-in Action Examples

AddAction (List + CreateView):

class AddAction(GeneralActionMixin, CreateViewActionMixin, BaseAction):
    label = 'Add'
    icon = 'plus'
    css_class = 'primary'

EditAction (Record + UpdateView):

class EditAction(RecordActionMixin, UpdateViewActionMixin, BaseAction):
    label = 'Edit'
    icon = 'pencil'
    css_class = 'primary'

DeleteAction (Record + DeleteView):

class DeleteAction(RecordActionMixin, DeleteViewActionMixin, BaseAction):
    label = 'Delete'
    icon = 'trash'
    css_class = 'danger'
    confirmation_required = True

DeleteBulkAction (Bulk + BulkDeleteView):

class DeleteBulkAction(BulkActionMixin, BulkDeleteViewActionMixin, BaseAction):
    label = 'Delete Selected'
    icon = 'trash'
    confirmation_required = True

Common Action Methods

These methods are commonly overridden in custom actions:

get_template_name()

def get_template_name(self) -> str | list[str]

Define which template(s) to use.

Returns: Template path or list of paths (tried in order)

Example:

def get_template_name(self):
    opts = self.model._meta
    return [
        f'djadmin/{opts.app_label}/{opts.model_name}_export.html',
        'djadmin/actions/export.html',
    ]

get_context_data()

def get_context_data(self, **kwargs) -> dict

Add custom context data to the template.

Parameters: **kwargs - Context from parent classes

Returns: dict - Template context

Example:

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['export_formats'] = ['csv', 'xlsx', 'json']
    return context

get_form_class()

def get_form_class(self) -> type[Form]

Define which form class to use (for FormView/CreateView/UpdateView actions).

Returns: Form class

Example:

def get_form_class(self):
    if self.model_admin.create_form_class:
        return self.model_admin.create_form_class
    return modelform_factory(self.model, fields='__all__')

get_queryset()

def get_queryset(self) -> QuerySet

Define queryset for ListView/DeleteView actions.

Returns: QuerySet

Example:

def get_queryset(self):
    qs = super().get_queryset()
    # Filter by user permissions
    if not self.request.user.is_superuser:
        qs = qs.filter(owner=self.request.user)
    return qs

get_success_url()

def get_success_url(self) -> str

Define redirect URL after successful form submission.

Returns: str - URL path

Example:

def get_success_url(self):
    opts = self.model._meta
    return reverse(
        f'admin:{opts.app_label}_{opts.model_name}_list',
        current_app=self.admin_site.name,
    )

Usage in ModelAdmin

Actions are configured in ModelAdmin through three lists:

from djadmin import ModelAdmin
from myapp.actions import ExportAction, ImportAction

class ProductAdmin(ModelAdmin):
    # Actions available from list view
    general_actions = [ListAction, AddAction, ExportAction, ImportAction]

    # Actions for multiple selected records
    bulk_actions = [DeleteBulkAction, BulkChangeStatusAction]

    # Actions for single records
    record_actions = [EditAction, DeleteAction, DuplicateAction]

See Also