diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 2099d14861..2913107b9c 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -858,15 +858,20 @@ class ModelAdmin(BaseModelAdmin): def _get_base_actions(self): """Return the list of actions, prior to any request-based filtering.""" actions = [] + base_actions = (self.get_action(action) for action in self.actions or []) + # get_action might have returned None, so filter any of those out. + base_actions = [action for action in base_actions if action] + base_action_names = {name for _, name, _ in base_actions} # Gather actions from the admin site first for (name, func) in self.admin_site.actions: + if name in base_action_names: + continue description = getattr(func, 'short_description', name.replace('_', ' ')) actions.append((func, name, description)) # Add actions from this ModelAdmin. - actions.extend(self.get_action(action) for action in self.actions or []) - # get_action might have returned None, so filter any of those out. - return filter(None, actions) + actions.extend(base_actions) + return actions def _filter_actions_by_permissions(self, request, actions): """Filter out any actions that the user doesn't have access to.""" diff --git a/tests/modeladmin/test_actions.py b/tests/modeladmin/test_actions.py index a33c1811b2..76f2f96c03 100644 --- a/tests/modeladmin/test_actions.py +++ b/tests/modeladmin/test_actions.py @@ -76,3 +76,42 @@ class AdminActionsTests(TestCase): ma2 = AdminB(Band, admin.AdminSite()) action_names = [name for _, name, _ in ma2._get_base_actions()] self.assertEqual(action_names, ['delete_selected']) + + def test_actions_replace_global_action(self): + def global_action_1(modeladmin, request, queryset): + pass + + def global_action_2(modeladmin, request, queryset): + pass + + global_action_1.short_description = 'Site-wide admin action 1.' + global_action_2.short_description = 'Site-wide admin action 2.' + admin.site.add_action(global_action_1, name='custom_action_1') + admin.site.add_action(global_action_2, name='custom_action_2') + + def custom_action_1(modeladmin, request, queryset): + pass + + custom_action_1.short_description = 'Local admin action 1.' + + class BandAdmin(admin.ModelAdmin): + actions = [custom_action_1, 'custom_action_2'] + + def custom_action_2(self, request, queryset): + pass + + custom_action_2.short_description = 'Local admin action 2.' + + ma = BandAdmin(Band, admin.site) + self.assertEqual(ma.check(), []) + self.assertEqual( + [ + desc + for _, name, desc in ma._get_base_actions() + if name.startswith('custom_action') + ], + [ + 'Local admin action 1.', + 'Local admin action 2.', + ], + )