#37263: Admin changelist search crashes (500) on `__exact` search_fields with
choices and over-matches on BooleanField
-------------------------------------------+------------------------------
Reporter: Adam Johnson | Owner: Adam Johnson
Type: Bug | Status: assigned
Component: contrib.admin | Version: 6.1
Severity: Release blocker | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------------+------------------------------
Regression in 4cecf3039586ea738afafb9a28c946bff42c37c1 (#36865), which
replaced `Cast`-based comparison of non-text `__exact` search fields with
per-term validation via the model field's `formfield().to_python()`.
That validation is insufficient for two kinds of fields:
**1. Crash (HTTP 500) for fields with choices**
For a model field with `choices` (e.g. `IntegerField(choices=...)`),
`formfield()` returns a `TypedChoiceField` whose `to_python()` returns the
raw string unvalidated.
The term then reaches the ORM and `IntegerField.get_prep_value()` raises
`ValueError: invalid literal for int() with base 10: 'john'`.
Since `ModelAdmin.get_search_results()` is called outside the
`IncorrectLookupParameters` handling in `ChangeList.get_queryset()`
(`django/contrib/admin/views/main.py`), the error propagates as a server
error.
Minimal repro:
{{{#!python
class Client(models.Model):
name = models.CharField(max_length=30)
status = models.IntegerField(choices=[(1, "Active"), (2, "Archived")])
class ClientAdmin(admin.ModelAdmin):
search_fields = ["name", "status__exact"]
}}}
Searching for `john` in the changelist returns HTTP 500 on 6.1 and main;
on 6.0 it returned the rows whose name matches.
**2. Over-matching for BooleanField**
For `BooleanField` `__exact` entries, `forms.BooleanField.to_python()`
maps almost any string to `True` (only `"false"`/`"0"` map to `False`;
nothing raises), so ''any'' search term OR-matches every row with a `True`
value.
{{{#!python
class Account(models.Model):
name = models.CharField(max_length=30)
active = models.BooleanField(default=True)
class AccountAdmin(admin.ModelAdmin):
search_fields = ["name", "active__exact"]
}}}
Searching for `john` returns every active account on 6.1 instead of just
`john` (6.0 behavior).
--
Ticket URL: <
https://code.djangoproject.com/ticket/37263>
Django <
https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.