0
# Django Object Actions
1
2
A Django app for adding object tools for models in the admin interface. Django Object Actions extends Django's admin action framework to work on individual model instances in addition to querysets, enabling custom actions on both change and changelist views.
3
4
## Package Information
5
6
- **Package Name**: django-object-actions
7
- **Language**: Python
8
- **Installation**: `pip install django-object-actions`
9
- **Django Compatibility**: Django admin framework required
10
- **Python Compatibility**: 3.9+
11
12
## Core Imports
13
14
```python
15
from django_object_actions import DjangoObjectActions, action, takes_instance_or_queryset
16
```
17
18
For advanced use cases:
19
20
```python
21
from django_object_actions import BaseDjangoObjectActions
22
```
23
24
## Basic Usage
25
26
```python
27
from django.contrib import admin
28
from django_object_actions import DjangoObjectActions, action
29
30
class ArticleAdmin(DjangoObjectActions, admin.ModelAdmin):
31
@action(label="Publish", description="Submit this article")
32
def publish_this(self, request, obj):
33
# Your custom logic here
34
obj.status = 'published'
35
obj.save()
36
self.message_user(request, f"Published {obj.title}")
37
38
change_actions = ('publish_this',)
39
changelist_actions = ()
40
41
admin.site.register(Article, ArticleAdmin)
42
```
43
44
## Setup Requirements
45
46
1. Add to Django settings:
47
```python
48
INSTALLED_APPS = [
49
# ... other apps
50
'django_object_actions',
51
# ... other apps
52
]
53
```
54
55
2. Inherit from `DjangoObjectActions` mixin in your `ModelAdmin` classes.
56
57
## Capabilities
58
59
### Admin Mixin Classes
60
61
Core mixin classes that extend Django ModelAdmin to support object actions.
62
63
#### BaseDjangoObjectActions
64
65
Base mixin providing object action functionality without default templates.
66
67
```python { .api }
68
class BaseDjangoObjectActions:
69
"""
70
ModelAdmin mixin to add new actions just like adding admin actions.
71
72
Attributes:
73
- change_actions: list of str - Action names for change view
74
- changelist_actions: list of str - Action names for changelist view
75
- tools_view_name: str - URL name for the actions view
76
"""
77
78
change_actions = []
79
changelist_actions = []
80
tools_view_name = None
81
82
def get_change_actions(self, request, object_id, form_url):
83
"""Override to customize what actions appear in change view."""
84
85
def get_changelist_actions(self, request):
86
"""Override to customize what actions appear in changelist view."""
87
```
88
89
#### DjangoObjectActions
90
91
Main mixin that includes admin templates and extends BaseDjangoObjectActions.
92
93
```python { .api }
94
class DjangoObjectActions(BaseDjangoObjectActions):
95
"""
96
Complete mixin with templates for Django admin object actions.
97
98
Attributes:
99
- change_form_template: str - Template for change form
100
- change_list_template: str - Template for changelist
101
"""
102
103
change_form_template = "django_object_actions/change_form.html"
104
change_list_template = "django_object_actions/change_list.html"
105
```
106
107
### Action Decorators
108
109
Decorators for enhancing action functions with metadata and behavior.
110
111
#### @action Decorator
112
113
Adds metadata and behavior configuration to action functions.
114
115
```python { .api }
116
def action(
117
function=None,
118
*,
119
permissions=None,
120
description=None,
121
label=None,
122
attrs=None,
123
methods=('GET', 'POST'),
124
button_type='a'
125
):
126
"""
127
Add attributes to an action function.
128
129
Parameters:
130
- permissions: list of str - Required permissions
131
- description: str - Tooltip description for the button
132
- label: str - Display label for the button (defaults to function name)
133
- attrs: dict - HTML attributes for the button element
134
- methods: tuple - Allowed HTTP methods (default: ('GET', 'POST'))
135
- button_type: str - Button type ('a' for link, 'form' for form submission)
136
137
Returns:
138
Decorated function with added attributes
139
"""
140
```
141
142
**Usage Examples:**
143
144
```python
145
@action(label="Publish Article", description="Mark this article as published")
146
def publish_article(self, request, obj):
147
obj.status = 'published'
148
obj.save()
149
150
@action(
151
permissions=['publish'],
152
description='Submit for publication',
153
attrs={'class': 'btn-primary'},
154
methods=('POST',),
155
button_type='form'
156
)
157
def submit_for_publication(self, request, obj):
158
obj.submit_for_review()
159
```
160
161
#### @takes_instance_or_queryset Decorator
162
163
Converts admin actions to work with both individual objects and querysets.
164
165
```python { .api }
166
def takes_instance_or_queryset(func):
167
"""
168
Make standard Django admin actions compatible with object actions.
169
170
Converts single model instances to querysets, allowing reuse of
171
existing admin actions as object actions.
172
173
Parameters:
174
- func: function - Admin action function that expects a queryset
175
176
Returns:
177
Decorated function that works with both instances and querysets
178
"""
179
```
180
181
**Usage Example:**
182
183
```python
184
@takes_instance_or_queryset
185
def mark_as_featured(self, request, queryset):
186
queryset.update(featured=True)
187
self.message_user(request, f"Marked {queryset.count()} items as featured")
188
189
# Can be used in both contexts:
190
change_actions = ['mark_as_featured'] # Works on single objects
191
actions = ['mark_as_featured'] # Works on querysets
192
```
193
194
### Action Implementation Patterns
195
196
#### Change Actions (Single Object)
197
198
Actions that operate on individual model instances in the change view.
199
200
```python
201
class MyModelAdmin(DjangoObjectActions, admin.ModelAdmin):
202
def my_change_action(self, request, obj):
203
"""
204
Action function for change view.
205
206
Parameters:
207
- request: HttpRequest - Django request object
208
- obj: Model instance - The object being acted upon
209
210
Returns:
211
- None: Redirects back to change view
212
- HttpResponse: Custom response (redirect, render, etc.)
213
"""
214
# Your logic here
215
obj.some_field = 'new_value'
216
obj.save()
217
218
# Optional: Send message to user
219
self.message_user(request, "Action completed successfully")
220
221
# Optional: Custom redirect
222
# return HttpResponseRedirect('/custom/url/')
223
224
change_actions = ('my_change_action',)
225
```
226
227
#### Changelist Actions (Queryset)
228
229
Actions that operate on querysets in the changelist view.
230
231
```python
232
class MyModelAdmin(DjangoObjectActions, admin.ModelAdmin):
233
def my_changelist_action(self, request, queryset):
234
"""
235
Action function for changelist view.
236
237
Parameters:
238
- request: HttpRequest - Django request object
239
- queryset: QuerySet - The selected objects
240
241
Returns:
242
- None: Redirects back to changelist view
243
- HttpResponse: Custom response
244
"""
245
# Bulk operation
246
updated = queryset.update(status='processed')
247
self.message_user(request, f"Updated {updated} items")
248
249
changelist_actions = ('my_changelist_action',)
250
```
251
252
#### Dynamic Action Control
253
254
Control which actions are available based on context.
255
256
```python
257
class MyModelAdmin(DjangoObjectActions, admin.ModelAdmin):
258
def get_change_actions(self, request, object_id, form_url):
259
"""
260
Dynamically determine available change actions.
261
262
Parameters:
263
- request: HttpRequest - Current request
264
- object_id: str - Primary key of the object
265
- form_url: str - URL of the change form
266
267
Returns:
268
list of str - Action names to display
269
"""
270
actions = list(super().get_change_actions(request, object_id, form_url))
271
272
# Example: Remove action based on user permissions
273
if not request.user.is_superuser:
274
actions = [a for a in actions if a != 'dangerous_action']
275
276
# Example: Remove action based on object state
277
obj = self.model.objects.get(pk=object_id)
278
if obj.status == 'published':
279
actions = [a for a in actions if a != 'publish_action']
280
281
return actions
282
283
def get_changelist_actions(self, request):
284
"""
285
Dynamically determine available changelist actions.
286
287
Parameters:
288
- request: HttpRequest - Current request
289
290
Returns:
291
list of str - Action names to display
292
"""
293
actions = list(super().get_changelist_actions(request))
294
295
# Example: Role-based action availability
296
if request.user.groups.filter(name='editors').exists():
297
actions.append('editor_special_action')
298
299
return actions
300
```
301
302
### Advanced Usage Patterns
303
304
#### Custom Response Handling
305
306
Actions can return custom HTTP responses for complex workflows.
307
308
```python
309
def complex_action(self, request, obj):
310
"""Action with custom response handling."""
311
from django.shortcuts import render
312
from django.http import HttpResponseRedirect
313
314
if request.method == 'POST':
315
# Process the action
316
obj.process_complex_operation()
317
self.message_user(request, "Operation completed")
318
return None # Redirect back to change view
319
320
# Show confirmation page
321
return render(request, 'admin/confirm_complex_action.html', {
322
'object': obj,
323
'title': 'Confirm Complex Action'
324
})
325
```
326
327
#### Integration with Existing Admin Actions
328
329
Reuse existing admin actions as object actions.
330
331
```python
332
class MyModelAdmin(DjangoObjectActions, admin.ModelAdmin):
333
def bulk_update_status(self, request, queryset):
334
"""Standard admin action."""
335
queryset.update(status='updated')
336
self.message_user(request, f"Updated {queryset.count()} items")
337
338
@takes_instance_or_queryset
339
def single_update_status(self, request, queryset):
340
"""Same logic, works for both single objects and querysets."""
341
return self.bulk_update_status(request, queryset)
342
343
# Available in both contexts
344
actions = ['bulk_update_status']
345
change_actions = ['single_update_status']
346
changelist_actions = ['bulk_update_status']
347
```
348
349
#### HTML Attribute Customization
350
351
Customize button appearance and behavior with HTML attributes.
352
353
```python
354
@action(
355
label="Delete Safely",
356
description="Safely delete this item with confirmation",
357
attrs={
358
'class': 'btn btn-danger',
359
'onclick': 'return confirm("Are you sure?");',
360
'data-toggle': 'tooltip',
361
'data-placement': 'top'
362
}
363
)
364
def safe_delete(self, request, obj):
365
obj.delete()
366
self.message_user(request, "Item deleted successfully")
367
```
368
369
## Error Handling
370
371
The framework handles common error scenarios:
372
373
- **Invalid action names**: Raises `Http404` if action doesn't exist
374
- **HTTP method restrictions**: Returns `HttpResponseNotAllowed` for invalid methods
375
- **Permission checking**: Integrates with Django admin's permission system
376
- **URL parameter handling**: Properly unquotes special characters in primary keys
377
378
## Template Integration
379
380
Django Object Actions provides three templates that extend Django's admin templates:
381
382
- `django_object_actions/change_form.html` - Adds action buttons to change view
383
- `django_object_actions/change_list.html` - Adds action buttons to changelist view
384
- `django_object_actions/action_trigger.html` - Renders individual action buttons
385
386
These templates automatically integrate with your existing admin customizations and preserve all Django admin functionality.