diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index d97597fe66dd..e93fdf40475d 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -475,24 +475,25 @@ def lookup_allowed(self, lookup, value, request=None): # Lookups on nonexistent fields are ok, since they're ignored # later. break + if not prev_field or ( + prev_field.is_relation + and field not in model._meta.parents.values() + and field is not model._meta.auto_field + and ( + model._meta.auto_field is None + or part not in getattr(prev_field, "to_fields", []) + ) + and (field.is_relation or not field.primary_key) + ): + relation_parts.append(part) if not getattr(field, "path_infos", None): # This is not a relational field, so further parts # must be transforms. break - if ( - not prev_field - or (field.is_relation and field not in model._meta.parents.values()) - or ( - prev_field.is_relation - and model._meta.auto_field is None - and part not in getattr(prev_field, "to_fields", []) - ) - ): - relation_parts.append(part) prev_field = field model = field.path_infos[-1].to_opts.model - if not relation_parts or len(parts) == 1: + if len(relation_parts) <= 1: # Either a local field filter, or no fields at all. return True valid_lookups = {self.date_hierarchy} diff --git a/docs/releases/5.0.3.txt b/docs/releases/5.0.3.txt index 30e87127b0b7..b433bf6f2fe7 100644 --- a/docs/releases/5.0.3.txt +++ b/docs/releases/5.0.3.txt @@ -15,3 +15,8 @@ Bugfixes * Fixed a bug in Django 5.0 that caused a crash of ``Signal.asend()`` and ``asend_robust()`` when all receivers were asynchronous functions (:ticket:`35174`). + +* Fixed a regression in Django 5.0.1 where :meth:`.ModelAdmin.lookup_allowed` + would prevent filtering against foreign keys using lookups like ``__isnull`` + when the field was not included in :attr:`.ModelAdmin.list_filter` + (:ticket:`35173`). diff --git a/tests/modeladmin/tests.py b/tests/modeladmin/tests.py index fad2dfaa1cde..de8d26ae4657 100644 --- a/tests/modeladmin/tests.py +++ b/tests/modeladmin/tests.py @@ -174,7 +174,19 @@ class PlaceAdmin(ModelAdmin): pass ma = PlaceAdmin(Place, self.site) - self.assertIs(ma.lookup_allowed("country", "1", request), True) + + cases = [ + ("country", "1"), + ("country__exact", "1"), + ("country__id", "1"), + ("country__id__exact", "1"), + ("country__isnull", True), + ("country__isnull", False), + ("country__id__isnull", False), + ] + for lookup, lookup_value in cases: + with self.subTest(lookup=lookup): + self.assertIs(ma.lookup_allowed(lookup, lookup_value, request), True) @isolate_apps("modeladmin") def test_lookup_allowed_non_autofield_primary_key(self):