-
-
Notifications
You must be signed in to change notification settings - Fork 32.8k
Fixed #12090 -- Show admin actions on the edit pages too #16012
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
464bbf2
to
07a2b76
Compare
django/contrib/admin/templates/admin/includes/edit_actions.html
Outdated
Show resolved
Hide resolved
Another point that is worth mentioning: Django Admin Inline Actions have the e.g @admin.action(description='Mark selected stories as published')
def make_published(modeladmin, request, queryset):
queryset.update(status='p') The problem this poses is that this attribute was always designed to be used as part of an action against multiple instances of a given model and thus over the years people (me..) would have used names written in the plural-form like "Regenerate contracts", or "Refresh indexes". Therefore using those names in the context of the edit page would cause them to not make sense because it's a singular object being "actioned" rather than multiple. Others would have followed this naming pattern too, especially as the documentation example uses the wording "Mark selected stories as published" which would not work in singular form. In an ideal world, inline actions would need two variables to do a good job of this - as Django does in many other places ( |
@mgaligniana Do you have time to keep working on this? |
@felixxm Yes! This week I'm going to add some progress! 🙏 Sorry, I've been busy these months. |
1108397
to
4a8cd47
Compare
5baf1fb
to
dd95290
Compare
047df76
to
8a1db0c
Compare
Has been passed almost 1 year! Sorry djm, but finally I've returned to work on this! 🙏
|
a255676
to
0022785
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some initial thoughts.
d626280
to
8ae5dbe
Compare
3cfe703
to
c23c3a4
Compare
9eb1d82
to
f7178ea
Compare
I've applied all the comments! + added couple of tests (I updated the PR description with more details) Let me know any test or change to add! 💪 I will not disappear again and I will try to finish it! |
🎗️ I've noticed we tagged as |
@mgaligniana the label is just so the selenium tests run. With that in mind I've also added the screenshots label. I'm not sure if a selenium test is needed or not. I guess it would be nice to write a test that selects an action and runs it, though I'm not sure if it's just duplicating the tests that are already written. I will try to do an accessibility review today as I'm interested in this getting landed :) |
Exactly ☝️ as this changes admin code, we want all admin tests to run and a number of these are selenium tests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a quick scan of the code but not too deep. I left some comments.
From an accessibility point of view I see no issues here because we're just re-using what already exists.
django/contrib/admin/options.py
Outdated
ordering. Otherwise don't specify the queryset, let the field decide | ||
ordering. Otherwise don't specify the queryset, let the field decide |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we usually try to avoid unrelated cleanup in PRs but I'll let the fellows be the judge, I don't really know.
django/contrib/admin/options.py
Outdated
selected_action = request.POST.get("action", "") | ||
if actions and selected_action: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we could use walrus operator here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As it's used only for the condition I think is better don't declare a variable and use it directly in the if. Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, brain fog, just confused that I saw it twice.
docs/ref/contrib/admin/actions.txt
Outdated
The actions will be available also in the detail view but the description should be | ||
adjusted to make sense we are talking about only one object:: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reads better like this to my native English brain:
"The actions will also be available in the detail view but the description should be adjusted to show we are only talking about a single object."
docs/ref/contrib/admin/actions.txt
Outdated
def make_published(modeladmin, request, queryset): | ||
pass | ||
|
||
It will looks like this: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will looks like this: | |
It will look like this: |
b81d16d
to
ad75876
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the updates @mgaligniana
We need a release note in 6.0 and I have a question about the API design 😁 but I think we're close
docs/ref/contrib/admin/actions.txt
Outdated
description to show we are only talking about a single object:: | ||
|
||
@admin.action( | ||
description="Mark selected story as published", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
description="Mark selected story as published", | |
description="Mark story as published", |
This would need an update to the image
I also think the image dropdown format is inconsistent with other dropdowns on the page (maybe a different operating system or browser) @nessita might be able to help here
django/contrib/admin/actions.py
Outdated
@@ -14,7 +14,8 @@ | |||
|
|||
@action( | |||
permissions=["delete"], | |||
description=gettext_lazy("Delete selected %(verbose_name_plural)s"), | |||
description=gettext_lazy("Delete"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
description=gettext_lazy("Delete"), | |
description=gettext_lazy("Delete %(verbose_name)s"), |
django/contrib/admin/options.py
Outdated
# If action was executed from change view, redirect to | ||
# list view once finished. | ||
is_change_view = request.resolver_match.url_name.endswith("change") | ||
return HttpResponseRedirect( | ||
reverse( | ||
"admin:%s_%s_changelist" | ||
% (self.opts.app_label, self.opts.model_name) | ||
) | ||
if is_change_view | ||
else request.get_full_path() | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I understand this is a nicer flow for the delete action which is provided by default, however I wonder if this is nicer for the other custom actions 🤔
The delete action on the single view is not that useful (as we already have a delete button when you have a delete permission) so I would question if we even should list it
Do you think we should have a boolean like changelist_only
that can be set on the action? Perhaps defaults to True
so folks need to opt in to the feature and actions like the delete case might not want to be on a singular level?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I understand this is a nicer flow for the delete action which is provided by default, however I wonder if this is nicer for the other custom actions 🤔
Makes sense. In the last commit I've deleted the logic I had added redirecting to the list view + I've added a success message.
The delete action on the single view is not that useful (as we already have a delete button when you have a delete permission) so I would question if we even should list it
Do you think we should have a boolean likechangelist_only
that can be set on the action? Perhaps defaults toTrue
so folks need to opt in to the feature and actions like the delete case might not want to be on a singular level?
Brilliant! I've added that changelist_only
option. I did it in a new commit so you can check easily the new changes + new test
I always forget the release notes! Arggh! 😆 Thank you Sarah! I'll read and apply the comments during this week, thank you! |
ad75876
to
8a0773c
Compare
permissions=None, | ||
description=None, | ||
description_plural=None, | ||
changelist_only=True, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎗️ I've added some documentation for this new parameter. But I would need help!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know changelist_only
was my suggestion, but I'm wondering if show_on_change_form
would be a better name with a default of False
. I think it's nicer to enable something than disable something
@@ -1648,23 +1652,16 @@ def response_action(self, request, queryset): | |||
|
|||
response = func(self, request, queryset) | |||
|
|||
self.message_user(request, "Action executed successfully", messages.SUCCESS) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎗️ For sure it could be better message or more detailed. I'll wait for comments!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I would be tempted to remove and allow folks to write their own message in their custom actions
b15ecad
to
dc30f94
Compare
This comment was marked as spam.
This comment was marked as spam.
This is so cool, thanks for working on this @mgaligniana , I've been waiting for this feature idk... since 2023? one more time, thank you!!! 🙌 🙌 🙌 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also need a release note
I made a suggestion to update changelist_only
to show_on_change_form
, we might want to get other peoples opinions on a range of names
Possible release notes in 6.0 admin section could be:
* The new ``show_on_change_form`` argument of
:func:`~django.contrib.admin.action` allows the action to be available on the
admin change form view. Defaults to ```False``.
* The new ``description_plural`` argument of
:func:`~django.contrib.admin.action` specifies a human-readable description
for actions applied to multiple objects on the admin changelist. Defaults to
the ``description`` value. This is useful when ``show_on_change_form`` is
set to ``True``, where ``description`` is then used for a single object on
the change form.
description='Mark selected stories as published', | ||
description='Mark story as published', | ||
description_plural='Mark selected stories as published', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we have changelist_only
, we either should add this to the example here or revert the current changes to the docstring as having description
and description_plural
together only makes sense when changelist_only=False
I think we can revert the changes to the docstring as it is just showing that two things are equivalent and I think this is sufficiently demonstrated in the existing example
@@ -1648,23 +1652,16 @@ def response_action(self, request, queryset): | |||
|
|||
response = func(self, request, queryset) | |||
|
|||
self.message_user(request, "Action executed successfully", messages.SUCCESS) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I would be tempted to remove and allow folks to write their own message in their custom actions
permissions=None, | ||
description=None, | ||
description_plural=None, | ||
changelist_only=True, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know changelist_only
was my suggestion, but I'm wondering if show_on_change_form
would be a better name with a default of False
. I think it's nicer to enable something than disable something
Actions are also available in the admin object detail page. We can adjust the | ||
description to show we are only talking about a single object:: | ||
|
||
@admin.action( | ||
description="Mark story as published", | ||
description_plural="Mark selected stories as published", | ||
) | ||
def make_published(modeladmin, request, queryset): | ||
pass | ||
|
||
This will give us an admin object detail page that looks like this: | ||
|
||
.. image:: _images/adding-actions-to-the-modeladmin-detail-view.png | ||
|
||
.. versionchanged:: 6.0 | ||
|
||
Actions were added to the admin object detail page by setting ``changelist_only`` to False. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(note I am running with the show_on_change_form
suggestion)
Actions are also available in the admin object detail page. We can adjust the | |
description to show we are only talking about a single object:: | |
@admin.action( | |
description="Mark story as published", | |
description_plural="Mark selected stories as published", | |
) | |
def make_published(modeladmin, request, queryset): | |
pass | |
This will give us an admin object detail page that looks like this: | |
.. image:: _images/adding-actions-to-the-modeladmin-detail-view.png | |
.. versionchanged:: 6.0 | |
Actions were added to the admin object detail page by setting ``changelist_only`` to False. | |
We can also make an action available in the admin change form by setting | |
`show_on_change_form=True``. We should adjust the descriptions for the single | |
object and multiple object cases:: | |
@admin.action( | |
description="Mark story as published", | |
description_plural="Mark selected stories as published", | |
show_on_change_form=True, | |
) | |
def make_published(modeladmin, request, queryset): | |
pass | |
This will give us an admin change form page that looks like this: | |
.. image:: _images/adding-actions-to-the-modeladmin-detail-view.png | |
.. versionchanged:: 6.0 | |
The ``show_on_change_form`` and ``description_plural`` arguments of | |
:func:`~django.contrib.admin.action` were added. |
@@ -434,7 +452,7 @@ For example:: | |||
The ``action`` decorator | |||
======================== | |||
|
|||
.. function:: action(*, permissions=None, description=None) | |||
.. function:: action(*, permissions=None, description=None, description_plural=None, changelist_only=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the bottom of this, I think it's worth describing the new parameters a bit more and we need a versionchanged note:
The ``show_on_change_form`` argument controls where the action is available
in the admin interface. When ``show_on_change_form`` is ``False``, the
action is only available on the admin changelist page. When
``show_on_change_form`` is ``True``, the action is available on the
admin changelist and the admin change form. Defaults to ``True``.
When ``show_on_change_form`` is ``True``, ``description`` is used for the
admin change form, and ``description_plural`` is used for the admin
changelist page. If ``description_plural`` is not provided, it falls back
to the ``description`` value.
.. versionchanged:: 6.0
The ``show_on_change_form`` and ``description_plural`` arguments were
added.
This suggestion assumes we change to using show_on_change_form
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Antoliny0919 (this PR needs rebasing) but I wonder if you can help update this screenshot as the dropdown here is inconsistent with other screenshots
Hi!
./runtests.py admin_views.test_actions.AdminDetailActionsTest
save_on_top
isTrue
looks like:test_available_detail_actions
-> Actions form displayed (with singular description)test_available_list_actions
-> Actions with plural descriptiontest_detail_actions_are_not_present_in_add_view
-> Actions form is not displayed in "Add" viewtest_update_external_subscriber_without_select_an_action
-> Select an action is not requiredtest_external_subscriber_is_not_updated_when_select_an_action
-> If I edit the object and then execute an action, the object is not changed/updatedtest_custom_function_action_streaming_response_change_view
-> Happy pathtest_action_with_permissions
-> Can't run action without permissionstest_redirect_after_an_action
-> Redirects to changelist view after ran an action