summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhuwaiza tahir <muhammadhuwaizatahir@gmail.com>2026-03-18 12:40:04 +0500
committerJacob Walls <jacobtylerwalls@gmail.com>2026-03-18 08:23:32 -0400
commit4b2b4bf0ac2707dc9c4d51cabfa72168eaea95fe (patch)
treedfac14e6c5501030a425c5bba9a9729592658395
parent2e33abe57c37dce6ce5eff9e6cbaf47cdddcf1e3 (diff)
Fixed #36926 -- Made admin use boolean icons for related BooleanFields in list_display.
When using related field lookups like 'parent__is_active' in list_display, the admin now correctly detects if the final field is a BooleanField and displays boolean icons instead of 'True'/'False' text. Modified lookup_field() in django/contrib/admin/utils.py to retrieve the final field from the path when traversing relations using LOOKUP_SEP (__), allowing display_for_field() to properly handle BooleanFields.
-rw-r--r--django/contrib/admin/utils.py5
-rw-r--r--tests/admin_changelist/models.py1
-rw-r--r--tests/admin_changelist/tests.py29
3 files changed, 34 insertions, 1 deletions
diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py
index e604a27750..3d5b023dea 100644
--- a/django/contrib/admin/utils.py
+++ b/django/contrib/admin/utils.py
@@ -294,6 +294,7 @@ def lookup_field(name, obj, model_admin=None):
except (FieldDoesNotExist, FieldIsAForeignKeyColumnName):
# For non-regular field values, the value is either a method,
# property, related field, or returned via a callable.
+ f = None
if callable(name):
attr = name
value = attr(obj)
@@ -312,10 +313,12 @@ def lookup_field(name, obj, model_admin=None):
attr = getattr(attr, part, sentinel)
if attr is sentinel:
return None, None, None
+ # The final field is needed for displaying boolean icons.
+ if LOOKUP_SEP in name:
+ f = get_fields_from_path(opts.model, name)[-1]
value = attr
if hasattr(model_admin, "model") and hasattr(model_admin.model, name):
attr = getattr(model_admin.model, name)
- f = None
else:
attr = None
value = getattr(obj, name)
diff --git a/tests/admin_changelist/models.py b/tests/admin_changelist/models.py
index 0b594300d2..d5d0633122 100644
--- a/tests/admin_changelist/models.py
+++ b/tests/admin_changelist/models.py
@@ -17,6 +17,7 @@ class Child(models.Model):
parent = models.ForeignKey(Parent, models.SET_NULL, editable=False, null=True)
name = models.CharField(max_length=30, blank=True)
age = models.IntegerField(null=True, blank=True)
+ is_active = models.BooleanField(default=True)
class GrandChild(models.Model):
diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py
index f051849449..ef0f8a0570 100644
--- a/tests/admin_changelist/tests.py
+++ b/tests/admin_changelist/tests.py
@@ -1768,6 +1768,35 @@ class ChangeListTests(TestCase):
cl = m.get_changelist_instance(request)
self.assertEqual(cl.get_ordering_field_columns(), {2: "asc"})
+ def test_list_display_related_field_boolean_display(self):
+ """
+ Related boolean fields (parent__is_active) display boolean icons.
+ """
+ parent = Parent.objects.create(name="Test Parent")
+ child_active = Child.objects.create(
+ name="Active Child", parent=parent, is_active=True
+ )
+ child_inactive = Child.objects.create(
+ name="Inactive Child", parent=parent, is_active=False
+ )
+
+ class GrandChildAdmin(admin.ModelAdmin):
+ list_display = ["name", "parent__is_active"]
+
+ GrandChild.objects.create(name="GrandChild of Active", parent=child_active)
+ GrandChild.objects.create(name="GrandChild of Inactive", parent=child_inactive)
+
+ m = GrandChildAdmin(GrandChild, custom_site)
+ request = self._mocked_authenticated_request("/grandchild/", self.superuser)
+ response = m.changelist_view(request)
+
+ # Boolean icons are rendered using img tags with specific alt text.
+ self.assertContains(response, 'alt="True"')
+ self.assertContains(response, 'alt="False"')
+ # Ensure "True" and "False" text are NOT in the response.
+ self.assertNotContains(response, ">True<")
+ self.assertNotContains(response, ">False<")
+
class GetAdminLogTests(TestCase):
def test_custom_user_pk_not_named_id(self):