diff --git a/AUTHORS b/AUTHORS index 2f342a832d..fefd251b19 100644 --- a/AUTHORS +++ b/AUTHORS @@ -405,6 +405,7 @@ answer newbie questions, and generally made Django that much better: Himanshu Chauhan hipertracker@gmail.com Hiroki Kiyohara + Hisham Mahmood Honza Král Horst Gutmann Hugo Osvaldo Barrera diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 89985357eb..1248c2c0df 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -996,7 +996,9 @@ class ModelAdmin(BaseModelAdmin): """ attrs = { "class": "action-select", - "aria-label": format_html(_("Select this object for an action - {}"), obj), + "aria-label": format_html( + _("Select this object for an action - {}"), str(obj) + ), } checkbox = forms.CheckboxInput(attrs, lambda value: False) return checkbox.render(helpers.ACTION_CHECKBOX_NAME, str(obj.pk)) diff --git a/docs/releases/5.0.8.txt b/docs/releases/5.0.8.txt index 8e072049b2..1037b78f75 100644 --- a/docs/releases/5.0.8.txt +++ b/docs/releases/5.0.8.txt @@ -11,3 +11,7 @@ Bugfixes * Added missing validation for ``UniqueConstraint(nulls_distinct=False)`` when using ``*expressions`` (:ticket:`35594`). + +* Fixed a regression in Django 5.0 where ``ModelAdmin.action_checkbox`` could + break the admin changelist HTML page when rendering a model instance with a + ``__html__`` method (:ticket:`35606`). diff --git a/tests/admin_changelist/admin.py b/tests/admin_changelist/admin.py index 8ffc45e391..02cf9a6df2 100644 --- a/tests/admin_changelist/admin.py +++ b/tests/admin_changelist/admin.py @@ -3,7 +3,7 @@ from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User from django.core.paginator import Paginator -from .models import Band, Child, Event, Parent, ProxyUser, Swallow +from .models import Band, Child, Event, GrandChild, Parent, ProxyUser, Swallow site = admin.AdminSite(name="admin") @@ -53,6 +53,13 @@ class ChildAdmin(admin.ModelAdmin): return super().get_queryset(request).select_related("parent") +class GrandChildAdmin(admin.ModelAdmin): + list_display = ["name"] + + +site.register(GrandChild, GrandChildAdmin) + + class CustomPaginationAdmin(ChildAdmin): paginator = CustomPaginator diff --git a/tests/admin_changelist/models.py b/tests/admin_changelist/models.py index aa4656e93e..78e65ab878 100644 --- a/tests/admin_changelist/models.py +++ b/tests/admin_changelist/models.py @@ -19,6 +19,17 @@ class Child(models.Model): age = models.IntegerField(null=True, blank=True) +class GrandChild(models.Model): + parent = models.ForeignKey(Child, models.SET_NULL, editable=False, null=True) + name = models.CharField(max_length=30, blank=True) + + def __str__(self): + return self.name + + def __html__(self): + return f'

{self.name}

' + + class Genre(models.Model): name = models.CharField(max_length=20) diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py index 4caefdb9e4..33b95bc841 100644 --- a/tests/admin_changelist/tests.py +++ b/tests/admin_changelist/tests.py @@ -43,6 +43,7 @@ from .admin import ( EmptyValueChildAdmin, EventAdmin, FilteredChildAdmin, + GrandChildAdmin, GroupAdmin, InvitationAdmin, NoListDisplayLinksParentAdmin, @@ -62,6 +63,7 @@ from .models import ( CustomIdUser, Event, Genre, + GrandChild, Group, Invitation, Membership, @@ -341,6 +343,27 @@ class ChangeListTests(TestCase): table_output, ) + def test_action_checkbox_for_model_with_dunder_html(self): + grandchild = GrandChild.objects.create(name="name") + request = self._mocked_authenticated_request("/grandchild/", self.superuser) + m = GrandChildAdmin(GrandChild, custom_site) + cl = m.get_changelist_instance(request) + cl.formset = None + template = Template( + "{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}" + ) + context = Context({"cl": cl, "opts": GrandChild._meta}) + table_output = template.render(context) + link = reverse( + "admin:admin_changelist_grandchild_change", args=(grandchild.id,) + ) + row_html = build_tbody_html(grandchild, link, "") + self.assertNotEqual( + table_output.find(row_html), + -1, + "Failed to find expected row element: %s" % table_output, + ) + def test_result_list_editable_html(self): """ Regression tests for #11791: Inclusion tag result_list generates a