summaryrefslogtreecommitdiff
path: root/tests/admin_changelist/tests.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/admin_changelist/tests.py')
-rw-r--r--tests/admin_changelist/tests.py72
1 files changed, 72 insertions, 0 deletions
diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py
index 7a50dbff77..e0772a3e6d 100644
--- a/tests/admin_changelist/tests.py
+++ b/tests/admin_changelist/tests.py
@@ -66,6 +66,7 @@ from .models import (
Group,
Invitation,
Membership,
+ MixedFieldsModel,
Musician,
OrderedObject,
Parent,
@@ -868,6 +869,77 @@ class ChangeListTests(TestCase):
cl = m.get_changelist_instance(request)
self.assertCountEqual(cl.queryset, [])
+ def test_exact_lookup_mixed_terms(self):
+ """
+ Multi-term search validates each term independently.
+
+ For 'foo 123' with search_fields=['name__icontains', 'age__exact']:
+ - 'foo': age lookup skipped (invalid), name lookup used
+ - '123': both lookups used (valid for age)
+ No Cast should be used; invalid lookups are simply skipped.
+ """
+ child = Child.objects.create(name="foo123", age=123)
+ Child.objects.create(name="other", age=456)
+ m = admin.ModelAdmin(Child, custom_site)
+ m.search_fields = ["name__icontains", "age__exact"]
+
+ request = self.factory.get("/", data={SEARCH_VAR: "foo 123"})
+ request.user = self.superuser
+
+ # One result matching on foo and 123.
+ cl = m.get_changelist_instance(request)
+ self.assertCountEqual(cl.queryset, [child])
+
+ # "xyz" - invalid for age (skipped), no match for name either.
+ request = self.factory.get("/", data={SEARCH_VAR: "xyz"})
+ request.user = self.superuser
+ cl = m.get_changelist_instance(request)
+ self.assertCountEqual(cl.queryset, [])
+
+ def test_exact_lookup_with_more_lenient_formfield(self):
+ """
+ Exact lookups on BooleanField use formfield().to_python() for lenient
+ parsing. Using model field's to_python() would reject 'false' whereas
+ the form field accepts it.
+ """
+ obj = UnorderedObject.objects.create(bool=False)
+ UnorderedObject.objects.create(bool=True)
+ m = admin.ModelAdmin(UnorderedObject, custom_site)
+ m.search_fields = ["bool__exact"]
+
+ # 'false' is accepted by form field but rejected by model field.
+ request = self.factory.get("/", data={SEARCH_VAR: "false"})
+ request.user = self.superuser
+
+ cl = m.get_changelist_instance(request)
+ self.assertCountEqual(cl.queryset, [obj])
+
+ def test_exact_lookup_validates_each_field_independently(self):
+ """
+ Each field validates the search term independently without leaking
+ converted values between fields.
+
+ "3." is valid for IntegerField (converts to 3) but invalid for
+ JSONField. The converted value must not leak to the JSONField check.
+ """
+ # obj_int has int_field=3, should match "3." via IntegerField.
+ obj_int = MixedFieldsModel.objects.create(
+ int_field=3, json_field={"key": "value"}
+ )
+ # obj_json has json_field=3, should NOT match "3." because "3." is
+ # invalid JSON.
+ MixedFieldsModel.objects.create(int_field=99, json_field=3)
+ m = admin.ModelAdmin(MixedFieldsModel, custom_site)
+ m.search_fields = ["int_field__exact", "json_field__exact"]
+
+ # "3." is valid for int (becomes 3) but invalid JSON.
+ # Only obj_int should match via int_field.
+ request = self.factory.get("/", data={SEARCH_VAR: "3."})
+ request.user = self.superuser
+
+ cl = m.get_changelist_instance(request)
+ self.assertCountEqual(cl.queryset, [obj_int])
+
def test_search_with_exact_lookup_for_non_string_field(self):
child = Child.objects.create(name="Asher", age=11)
model_admin = ChildAdmin(Child, custom_site)