Django Admin Actions
Django Admin's actions feature allows you to perform operations on multiple objects simultaneously. Actions are a powerful way to extend the Django Admin interface, enabling site administrators to perform bulk operations with just a few clicks.
Introduction
Actions in Django Admin appear as dropdown options above the list of objects in a change list page. The default action that comes with Django is "Delete selected objects," but you can create custom actions to perform any operation you need.
In this tutorial, you'll learn how to:
- Use the default delete action
- Create custom admin actions
- Apply actions to selected objects
- Register actions with your ModelAdmin
- Create site-wide actions
Understanding Default Actions
By default, Django provides a "delete selected objects" action that allows administrators to delete multiple objects at once.
Here's how it works:
- Select one or more objects using the checkboxes
- Choose "Delete selected objects" from the dropdown menu
- Click "Go"
- Confirm the deletion on the next page
This simple workflow saves administrators from having to delete objects one by one.
Creating Custom Admin Actions
Let's create a custom action that allows you to mark multiple blog posts as "published" at once.
Step 1: Define Your Action Function
An action is a function that takes three parameters:
modeladmin
: The currentModelAdmin
instancerequest
: The currentHttpRequest
objectqueryset
: AQuerySet
containing the selected objects
def make_published(modeladmin, request, queryset):
queryset.update(status='published')
make_published.short_description = "Mark selected posts as published"
The .short_description
attribute sets what appears in the dropdown menu.
Step 2: Register the Action with ModelAdmin
Once you've defined your action function, you need to register it with your ModelAdmin
:
from django.contrib import admin
from .models import BlogPost
class BlogPostAdmin(admin.ModelAdmin):
list_display = ('title', 'status', 'created_at')
actions = [make_published]
admin.site.register(BlogPost, BlogPostAdmin)
Now, the "Mark selected posts as published" action will appear in the dropdown menu alongside the default delete action.
Adding Message Confirmation
It's good practice to provide feedback to users when an action is performed. Let's enhance our action to display a message:
from django.contrib import messages
def make_published(modeladmin, request, queryset):
updated = queryset.update(status='published')
if updated == 1:
message = "1 post was"
else:
message = f"{updated} posts were"
messages.success(request, f"{message} successfully marked as published.")
make_published.short_description = "Mark selected posts as published"
Actions with Intermediate Pages
For more complex actions that require additional input or confirmation, you can return an HttpResponse
instead of updating objects directly:
from django.shortcuts import render
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
def archive_posts(modeladmin, request, queryset):
if request.POST.get('post'):
# User confirmed the action
archive_reason = request.POST.get('archive_reason')
for post in queryset:
post.status = 'archived'
post.archive_note = archive_reason
post.save()
modeladmin.message_user(
request,
f"{queryset.count()} posts were archived with reason: {archive_reason}"
)
return None
# First time the action is run, show a confirmation page
return render(
request,
'admin/archive_posts.html',
context={
'posts': queryset,
'media': modeladmin.media,
'action_checkbox_name': ACTION_CHECKBOX_NAME,
}
)
archive_posts.short_description = "Archive selected posts"
For this to work, you'll need to create a template file at templates/admin/archive_posts.html
:
{% extends "admin/base_site.html" %}
{% block content %}
<h1>Archive Posts</h1>
<p>You are about to archive the following posts:</p>
<ul>
{% for post in posts %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
<form action="" method="post">
{% csrf_token %}
<div>
<label for="archive_reason">Archive Reason:</label>
<input type="text" name="archive_reason" required>
</div>
{% for post in posts %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ post.pk }}">
{% endfor %}
<input type="hidden" name="action" value="archive_posts">
<input type="hidden" name="post" value="yes">
<input type="submit" value="Confirm Archive">
</form>
<a href="{% url 'admin:blogapp_blogpost_changelist' %}">Cancel</a>
{% endblock %}
Site-Wide Actions
If you want an action to be available across multiple models, you can create a site-wide action:
# In your main admin.py file
from django.contrib import admin
def mark_as_featured(modeladmin, request, queryset):
if hasattr(queryset.model, 'featured'):
queryset.update(featured=True)
modeladmin.message_user(request, "Selected items marked as featured.")
mark_as_featured.short_description = "Mark selected items as featured"
# Add it to the admin site's actions
admin.site.add_action(mark_as_featured)
Disabling Actions
You can disable the default delete action or any other action for a specific ModelAdmin
:
from django.contrib import admin
from .models import ImportantDocument
class ImportantDocumentAdmin(admin.ModelAdmin):
actions = None # Disables all actions
# OR
def get_actions(self, request):
actions = super().get_actions(request)
if 'delete_selected' in actions:
del actions['delete_selected'] # Only disable delete action
return actions
admin.site.register(ImportantDocument, ImportantDocumentAdmin)
Real-World Example: Export as CSV
Here's a practical example that shows how to create an action to export selected objects as a CSV file:
import csv
from django.http import HttpResponse
from django.contrib import admin
from .models import Customer
def export_as_csv(modeladmin, request, queryset):
meta = modeladmin.model._meta
field_names = [field.name for field in meta.fields]
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = f'attachment; filename={meta.verbose_name_plural}.csv'
writer = csv.writer(response)
writer.writerow(field_names)
for obj in queryset:
writer.writerow([getattr(obj, field) for field in field_names])
return response
export_as_csv.short_description = "Export selected objects as CSV"
class CustomerAdmin(admin.ModelAdmin):
list_display = ('name', 'email', 'phone', 'created_at')
actions = [export_as_csv]
admin.site.register(Customer, CustomerAdmin)
With this action, administrators can select customers and download their information as a CSV file.
Handling Permissions and Security
You can control who has access to specific actions using Django's permission system:
def make_featured(modeladmin, request, queryset):
# Check if user has permission
if not request.user.has_perm('blog.can_feature_posts'):
messages.error(request, "You do not have permission to feature posts.")
return
# Perform the action
queryset.update(featured=True)
messages.success(request, f"{queryset.count()} posts marked as featured.")
make_featured.short_description = "Mark selected posts as featured"
Summary
Django Admin actions provide a powerful way to perform bulk operations in your admin interface. Key points to remember:
- Actions are functions that take
modeladmin
,request
, andqueryset
parameters - You can add actions to specific
ModelAdmin
classes or make them site-wide - Actions can perform operations directly or show intermediate pages for additional input
- You can provide user feedback using Django's messaging framework
- Actions can be permission-controlled for security
By using admin actions effectively, you can significantly improve the usability of your Django admin interface and save time for your administrators.
Exercises
- Create an action that toggles the "active" status of selected users
- Build an action that assigns selected objects to a specific category
- Create an advanced action that generates a PDF report of selected items
- Implement an action with an intermediate page that allows bulk-updating of multiple fields
Additional Resources
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)