Introduction
Admin actions let users perform bulk operations on selected objects from the change list.
Key Concepts
Action function: A function that receives the ModelAdmin, request, and queryset.
@admin.action: Decorator for describing actions.
permissions: Control which users can perform actions.
Real World Context
In a content moderation system, moderators review hundreds of user-submitted posts daily. A 'Approve selected' action lets them select multiple posts and approve them in one click. A 'Reject and notify' action sends automated rejection emails to submitters. These bulk actions turn the admin change list into an efficient moderation queue that replaces what would otherwise require a custom internal tool.
Deep Dive
Basic Action
pythonfrom django.contrib import admin from .models import Article @admin.register(Article) class ArticleAdmin(admin.ModelAdmin): actions = ['make_published', 'make_draft'] @admin.action(description='Mark selected as published') def make_published(self, request, queryset): updated = queryset.update(status='published') self.message_user( request, f'{updated} articles marked as published.' ) @admin.action(description='Mark selected as draft') def make_draft(self, request, queryset): queryset.update(status='draft')
Action with Permission Check
python@admin.register(Article) class ArticleAdmin(admin.ModelAdmin): actions = ['publish_selected'] @admin.action( description='Publish selected articles', permissions=['publish'] ) def publish_selected(self, request, queryset): queryset.update(status='published') def has_publish_permission(self, request): return request.user.has_perm('blog.publish_article')
Action with Confirmation
pythonfrom django.contrib import admin, messages @admin.register(Article) class ArticleAdmin(admin.ModelAdmin): actions = ['delete_unpublished'] @admin.action(description='Delete all unpublished articles') def delete_unpublished(self, request, queryset): unpublished = queryset.filter(status='draft') count = unpublished.count() if count == 0: self.message_user(request, 'No drafts selected.', messages.WARNING) return unpublished.delete() self.message_user(request, f'Deleted {count} draft articles.')
Best Practices
- Use @admin.action decorator for descriptions and permissions.
- Provide feedback: Always call message_user() after an action.
Common Pitfalls
-
Not testing actions with large querysets: An action that iterates over queryset items individually (e.g., sending emails in a loop) can time out on large selections. Use queryset.update() for database changes and batch processing for external operations.
-
Forgetting the message_user() feedback: Actions that complete without feedback leave admins uncertain. Always call self.message_user() with a clear count of affected objects.
Summary
Admin actions process selected objects in bulk. Use the @admin.action decorator. Add permission checks with the permissions parameter.