fix: searching an Enum column raises UndefinedFunctionError

This commit is contained in:
2026-08-28 18:48:06 +02:00
committed by d3vyce
parent 23dc5c86b2
commit 1354f59bb4
2 changed files with 74 additions and 2 deletions
+4 -2
View File
@@ -160,9 +160,11 @@ def build_search_filters(
column = field column = field
# Build the filter (cast to String only when needed, to preserve # Build the filter (cast to String only when needed, to preserve
# pg_trgm GIN index usability on already-String columns) # pg_trgm GIN index usability on already-String columns).
column_as_string = ( column_as_string = (
column if isinstance(column.type, String) else column.cast(String) column
if isinstance(column.type, String) and not isinstance(column.type, Enum)
else column.cast(String)
) )
if config.case_sensitive: if config.case_sensitive:
filters.append(column_as_string.like(f"%{query}%")) filters.append(column_as_string.like(f"%{query}%"))
+70
View File
@@ -388,6 +388,76 @@ class TestBuildSearchFilters:
assert "CAST" in str(filters[0]) assert "CAST" in str(filters[0])
def test_casts_enum_column(self):
"""Enum subclasses String but maps to a native DB enum, which has no ILIKE."""
from fastapi_toolsets.crud.search import build_search_filters
filters, _ = build_search_filters(Order, "PEND", search_fields=[Order.status])
assert "CAST" in str(filters[0])
class TestSearchEnumColumn:
"""Searching an enum column must reach the database, not just build SQL."""
@pytest.mark.anyio
async def test_search_int_backed_enum(self, db_session: AsyncSession):
"""Enum(int, Enum) stores names, so the cast makes 'PEND' match PENDING."""
await OrderCrud.create(
db_session, OrderCreate(name="a", status=OrderStatus.PENDING)
)
await OrderCrud.create(
db_session, OrderCreate(name="b", status=OrderStatus.SHIPPED)
)
result = await OrderCrud.offset_paginate(
db_session,
search="PEND",
search_fields=[Order.status],
schema=OrderRead,
)
assert result.pagination.total_count == 1
assert result.data[0].status is OrderStatus.PENDING
@pytest.mark.anyio
async def test_search_str_backed_enum(self, db_session: AsyncSession):
"""Same for Enum(str, Enum) — still a native DB enum, still needs the cast."""
await OrderCrud.create(
db_session,
OrderCreate(name="a", status=OrderStatus.PENDING, color=Color.BLUE),
)
await OrderCrud.create(
db_session,
OrderCreate(name="b", status=OrderStatus.PENDING, color=Color.RED),
)
result = await OrderCrud.offset_paginate(
db_session,
search="BLU",
search_fields=[Order.color],
schema=OrderRead,
)
assert result.pagination.total_count == 1
assert result.data[0].color is Color.BLUE
@pytest.mark.anyio
async def test_search_mixed_enum_and_string_columns(self, db_session: AsyncSession):
"""An enum column alongside a plain String column (the get_searchable_fields shape)."""
await OrderCrud.create(
db_session, OrderCreate(name="widget", status=OrderStatus.SHIPPED)
)
result = await OrderCrud.offset_paginate(
db_session,
search="widget",
search_fields=[Order.name, Order.status, Order.color],
schema=OrderRead,
)
assert result.pagination.total_count == 1
class TestSearchConfig: class TestSearchConfig:
"""Tests for SearchConfig options.""" """Tests for SearchConfig options."""