diff options
| author | Rodrigo Vieira <rodrigo.vieira@gmail.com> | 2026-04-22 18:53:27 -0300 |
|---|---|---|
| committer | Jacob Walls <jacobtylerwalls@gmail.com> | 2026-04-22 22:22:55 -0400 |
| commit | fa2a3de6ede10b005fc2c1d23f4cffb53eaec425 (patch) | |
| tree | 2fbb49592272a9b5b54ae9457ba0e2072dabe74f /tests | |
| parent | a586f03f36f511064f171c0e30f4ca2ebfd60085 (diff) | |
Fixed #10919 -- Added delete_confirmation_max_display to ModelAdmin.
The new ModelAdmin.delete_confirmation_max_display attribute allows
limiting the number of related objects shown on the delete confirmation
page. When the limit is reached, a "…and N more objects." message is shown.
The feature relies on a new truncated_unordered_list template filter
added to django.contrib.admin.templatetags.admin_filters.
Thanks Jacob Tyler Walls for the review and guidance, Tobias McNulty for the report,
and terminator14 for the solution suggested.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/admin_views/customadmin.py | 16 | ||||
| -rw-r--r-- | tests/admin_views/test_actions.py | 13 | ||||
| -rw-r--r-- | tests/admin_views/tests.py | 20 | ||||
| -rw-r--r-- | tests/admin_views/urls.py | 1 | ||||
| -rw-r--r-- | tests/modeladmin/test_checks.py | 44 |
5 files changed, 93 insertions, 1 deletions
diff --git a/tests/admin_views/customadmin.py b/tests/admin_views/customadmin.py index a63d24a9ee..9621cb4d52 100644 --- a/tests/admin_views/customadmin.py +++ b/tests/admin_views/customadmin.py @@ -60,8 +60,19 @@ class CustomPwdTemplateUserAdmin(UserAdmin): class BookAdmin(admin.ModelAdmin): + delete_confirmation_max_display = 1 + def get_deleted_objects(self, objs, request): - return ["a deletable object"], {"books": 1}, set(), [] + return ( + ["a deletable object", "another object", "last object"], + {"books": 1}, + set(), + [], + ) + + +class BookAdminZeroDisplay(BookAdmin): + delete_confirmation_max_display = 0 site = Admin2(name="admin2") @@ -80,3 +91,6 @@ site.register(models.Simple, base_admin.AttributeErrorRaisingAdmin) simple_site = Admin2(name="admin4") simple_site.register(User, CustomPwdTemplateUserAdmin) + +zero_display_site = Admin2(name="admin_zero_display") +zero_display_site.register(models.Book, BookAdminZeroDisplay) diff --git a/tests/admin_views/test_actions.py b/tests/admin_views/test_actions.py index c2c18e0b74..de5f5462a3 100644 --- a/tests/admin_views/test_actions.py +++ b/tests/admin_views/test_actions.py @@ -220,6 +220,19 @@ class AdminActionsTest(TestCase): # BookAdmin.get_deleted_objects() returns custom text. self.assertContains(response, "a deletable object") + def test_delete_selected_uses_delete_confirmation_max_display(self): + book = Book.objects.create(name="Test Book") + data = { + ACTION_CHECKBOX_NAME: [book.pk], + "action": "delete_selected", + "index": 0, + } + response = self.client.post(reverse("admin2:admin_views_book_changelist"), data) + self.assertContains(response, "a deletable object") + self.assertContains(response, "…and 2 more objects.") + self.assertNotContains(response, "another object") + self.assertNotContains(response, "last object") + def test_custom_function_mail_action(self): """A custom action may be defined in a function.""" action_data = { diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 399c485fea..1fdb90822c 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -4198,6 +4198,26 @@ class AdminViewDeletedObjectsTest(TestCase): # BookAdmin.get_deleted_objects() returns custom text. self.assertContains(response, "a deletable object") + def test_delete_view_uses_delete_confirmation_max_display(self): + book = Book.objects.create(name="Test Book") + response = self.client.get( + reverse("admin2:admin_views_book_delete", args=(book.pk,)) + ) + self.assertContains(response, "a deletable object") + self.assertContains(response, "…and 2 more objects.") + self.assertNotContains(response, "another object") + self.assertNotContains(response, "last object") + + def test_delete_view_hides_objects_when_delete_confirmation_max_display_is_zero( + self, + ): + book = Book.objects.create(name="Test Book") + response = self.client.get( + reverse("admin_zero_display:admin_views_book_delete", args=(book.pk,)) + ) + self.assertNotContains(response, "<h2>Objects</h2>") + self.assertNotContains(response, 'id="deleted-objects"') + @override_settings(ROOT_URLCONF="admin_views.urls") class TestGenericRelations(TestCase): diff --git a/tests/admin_views/urls.py b/tests/admin_views/urls.py index 3c43b8721d..0ebe688cfc 100644 --- a/tests/admin_views/urls.py +++ b/tests/admin_views/urls.py @@ -35,6 +35,7 @@ urlpatterns = [ path("test_admin/admin11/", admin.site11.urls), path("test_admin/admin12/", admin.site12.urls), path("test_admin/admin13/", admin.site13.urls), + path("test_admin/admin_zero_display/", customadmin.zero_display_site.urls), path("test_admin/has_permission_admin/", custom_has_permission_admin.site.urls), path("test_admin/autocomplete_admin/", autocomplete_site.urls), # Shares the admin URL prefix. diff --git a/tests/modeladmin/test_checks.py b/tests/modeladmin/test_checks.py index c493148eb9..496e46e8b0 100644 --- a/tests/modeladmin/test_checks.py +++ b/tests/modeladmin/test_checks.py @@ -1020,6 +1020,50 @@ class ListMaxShowAllCheckTests(CheckTestCase): self.assertIsValid(TestModelAdmin, ValidationTestModel) +class DeleteConfirmationMaxObjectsCheckTests(CheckTestCase): + def test_not_integer(self): + class TestModelAdmin(ModelAdmin): + delete_confirmation_max_display = "hello" + + self.assertIsInvalid( + TestModelAdmin, + ValidationTestModel, + ( + "The value of " + "'delete_confirmation_max_display'" + " must be a non-negative integer or None." + ), + "admin.E131", + ) + + def test_negative_integer(self): + class TestModelAdmin(ModelAdmin): + delete_confirmation_max_display = -1 + + self.assertIsInvalid( + TestModelAdmin, + ValidationTestModel, + ( + "The value of " + "'delete_confirmation_max_display'" + " must be a non-negative integer or None." + ), + "admin.E131", + ) + + def test_valid_case(self): + class TestModelAdmin(ModelAdmin): + delete_confirmation_max_display = 100 + + self.assertIsValid(TestModelAdmin, ValidationTestModel) + + def test_valid_none(self): + class TestModelAdmin(ModelAdmin): + delete_confirmation_max_display = None + + self.assertIsValid(TestModelAdmin, ValidationTestModel) + + class SearchFieldsCheckTests(CheckTestCase): def test_not_iterable(self): class TestModelAdmin(ModelAdmin): |
