summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Kestenholz <mk@feinheit.ch>2018-11-10 00:52:30 +0100
committerTim Graham <timograham@gmail.com>2018-11-09 18:52:30 -0500
commitf9ff1df1daac8ae1fc22b27f48735148cb5488dd (patch)
tree87ff7dbda0dfcb0a896c892ef441cffa4f8c401f
parenta375e911efcc78054d09ef31422e081507e56623 (diff)
Fixed #29917 -- Stopped collecting ModelAdmin.actions from base ModelAdmins.
-rw-r--r--django/contrib/admin/options.py9
-rw-r--r--docs/ref/contrib/admin/actions.txt6
-rw-r--r--docs/releases/2.2.txt21
-rw-r--r--tests/modeladmin/test_actions.py21
4 files changed, 46 insertions, 11 deletions
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 241d22e82a..3a228516cc 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -859,13 +859,8 @@ class ModelAdmin(BaseModelAdmin):
for (name, func) in self.admin_site.actions:
description = getattr(func, 'short_description', name.replace('_', ' '))
actions.append((func, name, description))
-
- # Then gather them from the model admin and all parent classes,
- # starting with self and working back up.
- for klass in self.__class__.mro()[::-1]:
- class_actions = getattr(klass, 'actions', []) or []
- actions.extend(self.get_action(action) for action in class_actions)
-
+ # 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)
diff --git a/docs/ref/contrib/admin/actions.txt b/docs/ref/contrib/admin/actions.txt
index 82ce1e6f92..015427054a 100644
--- a/docs/ref/contrib/admin/actions.txt
+++ b/docs/ref/contrib/admin/actions.txt
@@ -343,10 +343,8 @@ Conditionally enabling or disabling actions
This returns a dictionary of actions allowed. The keys are action names, and
the values are ``(function, name, short_description)`` tuples.
- Most of the time you'll use this method to conditionally remove actions from
- the list gathered by the superclass. For example, if I only wanted users
- whose names begin with 'J' to be able to delete objects in bulk, I could do
- the following::
+ For example, if you only want users whose names begin with 'J' to be able
+ to delete objects in bulk::
class MyModelAdmin(admin.ModelAdmin):
...
diff --git a/docs/releases/2.2.txt b/docs/releases/2.2.txt
index 49fa9b0ba4..37ef7fd3a8 100644
--- a/docs/releases/2.2.txt
+++ b/docs/releases/2.2.txt
@@ -293,6 +293,27 @@ Database backend API
* Third party database backends must implement support for partial indexes or
set ``DatabaseFeatures.supports_partial_indexes`` to ``False``.
+Admin actions are no longer collected from base ``ModelAdmin`` classes
+----------------------------------------------------------------------
+
+For example, in older versions of Django::
+
+ from django.contrib import admin
+
+ class BaseAdmin(admin.ModelAdmin):
+ actions = ['a']
+
+ class SubAdmin(BaseAdmin):
+ actions = ['b']
+
+``SubAdmin`` will have actions ``'a'`` and ``'b'``.
+
+Now ``actions`` follows standard Python inheritance. To get the same result as
+before::
+
+ class SubAdmin(BaseAdmin):
+ actions = BaseAdmin.actions + ['b']
+
:mod:`django.contrib.gis`
-------------------------
diff --git a/tests/modeladmin/test_actions.py b/tests/modeladmin/test_actions.py
index 17b3c324d2..a33c1811b2 100644
--- a/tests/modeladmin/test_actions.py
+++ b/tests/modeladmin/test_actions.py
@@ -55,3 +55,24 @@ class AdminActionsTests(TestCase):
mock_request.user = user
actions = ma.get_actions(mock_request)
self.assertEqual(list(actions.keys()), expected)
+
+ def test_actions_inheritance(self):
+ class AdminBase(admin.ModelAdmin):
+ actions = ['custom_action']
+
+ def custom_action(modeladmin, request, queryset):
+ pass
+
+ class AdminA(AdminBase):
+ pass
+
+ class AdminB(AdminBase):
+ actions = None
+
+ ma1 = AdminA(Band, admin.AdminSite())
+ action_names = [name for _, name, _ in ma1._get_base_actions()]
+ self.assertEqual(action_names, ['delete_selected', 'custom_action'])
+ # `actions = None` removes actions from superclasses.
+ ma2 = AdminB(Band, admin.AdminSite())
+ action_names = [name for _, name, _ in ma2._get_base_actions()]
+ self.assertEqual(action_names, ['delete_selected'])