Repository: django/django
File: tests/admin_changelist/tests.py (Python)

Code before:
    def test_custom_lookup_with_pk_shortcut(self):
        self.assertEqual(CharPK._meta.pk.name, "char_pk")  # Not equal to 'pk'.
        m = admin.ModelAdmin(CustomIdUser, custom_site)

        abc = CharPK.objects.create(char_pk="abc")
        abcd = CharPK.objects.create(char_pk="abcd")
        m = admin.ModelAdmin(CharPK, custom_site)
        m.search_fields = ["pk__exact"]

        request = self.factory.get("/", data={SEARCH_VAR: "abc"})
        request.user = self.superuser
        cl = m.get_changelist_instance(request)
        self.assertCountEqual(cl.queryset, [abc])

        request = self.factory.get("/", data={SEARCH_VAR: "abcd"})
        request.user = self.superuser
        cl = m.get_changelist_instance(request)
        self.assertCountEqual(cl.queryset, [abcd])

    def test_pk_exact_lookup_does_not_use_cast(self):
        """Primary key exact lookups should not use Cast to preserve index usage."""
        child = Child.objects.create(name="Test", age=10)
        m = admin.ModelAdmin(Child, custom_site)
        m.search_fields = ["pk__exact"]

        request = self.factory.get("/", data={SEARCH_VAR: str(child.pk)})
        request.user = self.superuser

        with CaptureQueriesContext(connection) as context:
            cl = m.get_changelist_instance(request)
            self.assertCountEqual(cl.queryset, [child])

        # Verify no Cast is used in the query (would appear as ::varchar on
        # PostgreSQL or CAST on other databases).
        search_query = context.captured_queries[0]["sql"]
        self.assertNotIn("::varchar", search_query.lower())
        self.assertNotIn("cast(", search_query.lower())

    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)

        for search_term, expected_result in [
            ("11", [child]),
            ("Asher", [child]),
            ("1", []),
            ("A", []),
            ("random", []),
        ]:
            request = self.factory.get("/", data={SEARCH_VAR: search_term})
            request.user = self.superuser

Review comment (bug):
This is re-introducing the admin crash problem ticket-26001 fixed which you can demonstrate by doing


```suggestion
        request = self.factory.get("/", data={SEARCH_VAR: "foo"})
```

A cast (or an exclusion of terms) is necessary when dealing with string that are not string representation of integers.

Code after:

    def test_custom_lookup_with_pk_shortcut(self):
        self.assertEqual(CharPK._meta.pk.name, "char_pk")  # Not equal to 'pk'.
        m = admin.ModelAdmin(CustomIdUser, custom_site)

        abc = CharPK.objects.create(char_pk="abc")
        abcd = CharPK.objects.create(char_pk="abcd")
        m = admin.ModelAdmin(CharPK, custom_site)
        m.search_fields = ["pk__exact"]

        request = self.factory.get("/", data={SEARCH_VAR: "abc"})
        request.user = self.superuser
        cl = m.get_changelist_instance(request)
        self.assertCountEqual(cl.queryset, [abc])

        request = self.factory.get("/", data={SEARCH_VAR: "abcd"})
        request.user = self.superuser
        cl = m.get_changelist_instance(request)
        self.assertCountEqual(cl.queryset, [abcd])

    def test_exact_lookup_with_invalid_value(self):
        Child.objects.create(name="Test", age=10)
        m = admin.ModelAdmin(Child, custom_site)
        m.search_fields = ["pk__exact"]

        request = self.factory.get("/", data={SEARCH_VAR: "foo"})
        request.user = self.superuser

        # Invalid values are gracefully ignored.
        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)