Extension for the Django admin panel that allows passing additional parameters to actions by creating intermediate pages with forms.
Project description
django-admin-action-forms
Extension for the Django admin panel that allows passing additional parameters to actions by creating intermediate pages with forms.
🚀 Overview
Do you need confirmation pages for your actions in Django admin?
Does creating multiple actions in Django admin that only differ in arguments sound familiar?
Have you ever added a somewhat hacky way to pass additional parameters to an action?
If so, this package is for you!
This is how it looks in action:
By adding a few lines of code, you can create actions with custom forms that will be displayed in an intermediate page before the action is executed. Data from the form will be passed to the action as an additional argument.
Simple and powerful!
🎉 Features
- Requires minimal configuration, easy to use
- Supports all modern Django versions (3.2.x, 4.x.x, 5.x.x)
- Built on top of Django's templates and forms, matches the Django admin style
- No additional dependencies
- Supports
fields
/fieldsets
,filter_horizontal
/filter_vertical
andautocomplete_fields
- Works with custom widgets, validators and other Django form features
- Compatible with django-no-queryset-admin-actions
🔌 Installation
-
Install using
pip
:$ pip3 install django-admin-action-forms
-
Add
'django_admin_action_forms'
to yourINSTALLED_APPS
setting.INSTALLED_APPS = [ ... 'django_admin_action_forms', ]
-
Include
'django_admin_action_forms.urls'
in yoururls.py
file. This is needed only if you want to use autocomplete.If you want to include them under the same path as admin site, make sure to place them before the admin URLs.
from django.contrib import admin from django.urls import path, include urlpatterns = [ path("admin/action-forms/", include("django_admin_action_forms.urls")), path("admin/", admin.site.urls), ... ]
...or include them under any other path.
from django.contrib import admin from django.urls import path, include urlpatterns = [ path("admin/", admin.site.urls), ... path("any/other/path/", include("django_admin_action_forms.urls")), ]
✏️ Examples
Simple confirm form
Sometimes you do not need any additional parameters, but you want to display a confirmation form before executing the action, just to make sure the user is aware of what they are doing. By default, Django displays such form for the built-in delete_selected
action.
Let's create a simple action that will reset the password for selected users, but before that, we want to display a confirmation form.
from django_admin_action_forms import action_with_form, AdminActionForm
class ResetUsersPasswordActionForm(AdminActionForm):
# No fields needed
class Meta:
list_objects = True
help_text = "Are you sure you want proceed with this action?"
@action_with_form(
ResetUsersPasswordActionForm,
description="Reset password for selected users",
)
def reset_users_password_action(modeladmin, request, queryset, data):
modeladmin.message_user(request, f"Password reset for {queryset.count()} users.")
By doing this, we recreated the behavior of intermediate page from the built-in delete_selected
action.
Action with parameters
In many cases however, you will want to pass additional parameters to the action. This can be very useful for e.g.:
- Changing the status of
Order
to one of the predefined values - Setting a discount that you input for selected
Product
objects - Adding multiple tags to selected
Article
objects at once - Sending mails to selected
User
objects with a custom message, title and attachments
...and many more!
Let's create an action that will change the status of selected Order
to a value that we select using a dropdown.
from django import forms
from django_admin_action_forms import action_with_form, AdminActionForm
class ChangeOrderStatusActionForm(AdminActionForm):
status = forms.ChoiceField(
label="Status",
choices=[("new", "New"), ("processing", "Processing"), ("completed", "Completed")],
required=True,
)
@action_with_form(
ChangeOrderStatusActionForm,
description="Change status for selected Orders",
)
def change_order_status_action(modeladmin, request, queryset, data):
for order in queryset:
order.status = data["status"]
order.save()
modeladmin.message_user(request, f'Status changed to {data["status"].upper()} for {queryset.count()} orders.')
You may think that this could be achieved by creating an action for each status, but what if you have 10 statuses? 100? This way you can create a single action that will work for all of them.
And how about parameter, that is not predefined, like a date or a number? It would be impossible to create an action for each possible value.
Let's create an action form that will accept a discount for selected Products
and a date when the discount will end.
from django import forms
from django_admin_action_forms import action_with_form, AdminActionForm
class SetProductDiscountActionForm(AdminActionForm):
discount = forms.DecimalField(
label="Discount (%)",
min_value=0,
max_value=100,
decimal_places=2,
required=True,
)
valid_until = forms.DateField(
label="Valid until",
required=True,
)
Now we can set any discount and any date, and because we subclassed AdminActionForm
, we get a nice date picker.
Customizing action form layout
If your form has many fields, you may want to group them into fieldsets or reorder them. You can do this by using the fields
, fieldsets
, or corresponding methods in Meta
.
For Model
-related fields, it might be useful to use filter_horizontal
/filter_vertical
or autocomplete_fields
.
Let's create an action form for action that assigns selected Tasks
to Employee
, that we will select using autocomplete widget.
Also, let's add the field for setting the optional Tags
for selected Tasks
, and validate that no more than 3
are selected using Django's form validation.
from django import forms
from django_admin_action_forms import action_with_form, AdminActionForm
class AssignToEmployeeActionForm(AdminActionForm):
employee = forms.ModelChoiceField(
queryset=Employee.objects.all(),
required=True,
)
tags = forms.ModelMultipleChoiceField(
queryset=Tag.objects.all(),
required=False,
)
def clean_tags(self):
tags = self.cleaned_data["tags"]
if tags.count() > 3:
raise forms.ValidationError("You can't assign more than 3 tags to a task.")
return tags
class Meta:
autocomplete_fields = ["employee"]
filter_horizontal = ["tags"]
📄 Documentation
@action_with_form(form_class, *, permissions=None, description=None)
Works similar to
@admin.action
Decorator that can be used instead of @admin.action
to create action with custom form.
Functions decorated with @action_with_form
should accept additional argument data
that will contain cleaned data from the form, permissions
and description
work the same.
@action_with_form(
CustomActionForm,
description="Description of the action",
)
def custom_action(self, request, queryset, data):
value_of_field1 = data["field1"]
optional_value_of_field2 = data.get("field2")
...
ActionForm
Works similar to
Form
Base class for creating action forms responsible for all under the hood logic. Nearly always you will want to subclass AdminActionForm
instead of ActionForm
, as it provides additional features.
def __post_init__(modeladmin, request, queryset)
Added in version 1.2.0
Method called after the form is initialized that can be used to modify the form fields, add additional ones or change the form's Meta
options based on the modeladmin
from which the action was called, request
and queryset
containing objects on which the action will be performed.
def __post_init__(self, modeladmin, request, queryset):
modeladmin.message_user(
request, f"Warning, this action cannot be undone.", "warning"
)
if request.user.is_superuser:
self.fields["field1"].required = False
if queryset.count() > 25:
self.Meta.list_objects = False
AdminActionForm
In addition to ActionForm
, it replaces default widgets for most field types with corresponding Django admin widgets that e.g. add a interactive date picker or prepend a clickable link above URL fields.
Most of the time this is a class you want to subclass when creating custom action forms.
class CustomActionForm(AdminActionForm):
field1 = forms.ChoiceField(
label="Field 1",
choices=[(1, "Option 1"), (2, "Option 2"), (3, "Option 3")],
)
field2 = forms.CharField(
label="Field 2",
required=False,
widget=forms.TextInput
)
field3 = forms.DateField(label="Field 3", initial="2024-07-15")
...
ActionForm.Meta
Works similar to some
ModelAdmin
options
Additional configuration for action forms. It can be used to customize the layout of the form, add help text, or display a list of objects that will be affected by the action.
class CustomActionForm(AdminActionForm):
...
class Meta:
list_objects = True
help_text = "This is a help text"
...
Below you can find all available options:
list_objects
Default: False
If True
, the intermediate page will display a list of objects that will be affected by the action similarly
to the intermediate page for built-in delete_selected
action.
list_objects = True
help_text
Default: None
Text displayed between the form and the list of objects or form in the intermediate page.
help_text = "This text will be displayed between the form and the list of objects"
fields
Works similar to
ModelAdmin.fields
Default: None
Specifies the fields that should be displayed in the form. If fieldsets
is provided, fields
will be ignored.
fields = ["field1", ("field2", "field3")]
def get_fields(request)
Works similar to
ModelAdmin.get_fields()
Method that can be used to dynamically determine fields that should be displayed in the form. Can be used to reorder, group or exclude fields based on the request
. Should return a list of fields, as described above in the fields
.
@classmethod
def get_fields(cls, request):
if request.user.is_superuser:
return ["field1", "field2", "field3"]
else:
return ["field1", "field2"]
fieldsets
Works similar to
ModelAdmin.fieldsets
Default: None
If both fields
and fieldsets
are provided, fieldsets
will be used.
fieldsets = [
(
None,
{
"fields": ["field1", "field2", ("field3", "field4")],
},
),
(
"Fieldset 2",
{
"classes": ["collapse"],
"fields": ["field5", ("field6", "field7")],
"description": "This is a description for fieldset 2",
},
),
]
def get_fieldsets(request)
Works similar to
ModelAdmin.get_fieldsets()
Method that can be used to dynamically determine fieldsets that should be displayed in the form. Can be used to reorder, group or exclude fields based on the request
. Should return a list of fieldsets, as described above in the fieldsets
.
@classmethod
def get_fieldsets(cls, request):
if request.user.is_superuser:
return [
(
None,
{
"fields": ["field1", "field2", ("field3", "field4")],
},
),
(
"Fieldset 2",
{
"classes": ["collapse"],
"fields": ["field5", ("field6", "field7")],
"description": "This is a description for fieldset 2",
},
),
]
else:
return [
(
None,
{
"fields": ["field1", "field2", ("field3", "field4")],
},
),
]
[!NOTE] Only one of
get_fieldsets
,fieldsets
,get_fields
orfields
should be defined in theMeta
class. The order of precedence, from highest to lowest, is from left to right.
filter_horizontal
Works similar to
ModelAdmin.filter_horizontal
Default: None
Sets fields that should use horizontal filter widget. It should be a list of field names.
filter_horizontal = ["field1", "field2"]
filter_vertical
Works similar to
ModelAdmin.filter_vertical
Default: None
Sets fields that should use vertical filter widget. It should be a list of field names.
filter_vertical = ["field1", "field2"]
autocomplete_fields
Works similar to
ModelAdmin.autocomplete_fields
Default: None
Sets fields that should use autocomplete widget. It should be a list of field names.
autocomplete_fields = ["field1", "field2"]
[!NOTE] Autocomplete requires including
'django_admin_action_forms.urls'
in yoururls.py
file. See 🔌 Installation.
confirm_button_text
Added in version 1.2.0
Default: "Confirm"
Text displayed on the confirm button. It can be either a str
or a lazy translation.
from django.utils.translation import gettext_lazy as _
confirm_button_text = _("Proceed")
cancel_button_text
Added in version 1.2.0
Default: "Cancel"
Text displayed on the cancel button. It can be either a str
or a lazy translation.
from django.utils.translation import gettext_lazy as _
cancel_button_text = _("Abort")
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file django_admin_action_forms-1.2.3.tar.gz
.
File metadata
- Download URL: django_admin_action_forms-1.2.3.tar.gz
- Upload date:
- Size: 3.8 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | edc551e7f0c63d69cec5e678fa910f91bf52856b5e3b14687354898002e2d369 |
|
MD5 | c858fb9f083d0933db9e88850a906162 |
|
BLAKE2b-256 | 6d35263673763367167f59c30a0e3e72a3e49cd224aa13c072a180ad7e9eb393 |
File details
Details for the file django_admin_action_forms-1.2.3-py3-none-any.whl
.
File metadata
- Download URL: django_admin_action_forms-1.2.3-py3-none-any.whl
- Upload date:
- Size: 25.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | b9dc8bb829381315f914e61d8b12c2a13b980e8aa69a367d68c793dacb70939d |
|
MD5 | 7fdab9510bb68f3a4a25f6ec373556bc |
|
BLAKE2b-256 | 5681ce162afa3fc9c7f1845f777a529b77fbf5d1a8d5e66572a81b2e7da9d19f |