diff options
Diffstat (limited to 'tests/admin_changelist/tests.py')
| -rw-r--r-- | tests/admin_changelist/tests.py | 72 |
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) |
