fix: type the ids filter from its configured id type
advanced_alchemy annotates the `ids` query parameter as list[str] whatever the config says, so a bigint primary key was compared against strings and Postgres refused. Nothing had called ?ids= until now.
This commit is contained in:
@@ -127,6 +127,31 @@ def create_book_filter_dependencies(
|
||||
# Get base filters first
|
||||
filters = create_filter_dependencies(config, dep_defaults)
|
||||
|
||||
# OVERRIDE: id filter typed by the configured id type, not always `str`
|
||||
#
|
||||
# advanced_alchemy's `provide_id_filter` annotates `ids` as `list[str]` and
|
||||
# ignores `config["id_filter"]` entirely, so `?ids=12` reaches the database as
|
||||
# the string "12" and Postgres refuses `bigint = character varying`. Nothing
|
||||
# called `?ids=` until the duplicates screen needed to fetch a handful of books
|
||||
# by id, which is why it went unnoticed.
|
||||
if id_type := config.get("id_filter"):
|
||||
id_field = config.get("id_field", "id")
|
||||
|
||||
def provide_typed_id_filter(
|
||||
ids=Parameter(query="ids", default=None, required=False),
|
||||
) -> CollectionFilter:
|
||||
return CollectionFilter(field_name=id_field, values=ids)
|
||||
|
||||
# Attached as a type object rather than written as an annotation: this module
|
||||
# has `from __future__ import annotations`, so a written one is stored as the
|
||||
# string "Optional[list[id_type]]" and resolved against module globals, where
|
||||
# a local named `id_type` does not exist.
|
||||
provide_typed_id_filter.__annotations__["ids"] = Optional[list[id_type]]
|
||||
|
||||
filters[dep_defaults.ID_FILTER_DEPENDENCY_KEY] = Provide(
|
||||
provide_typed_id_filter, sync_to_thread=False
|
||||
)
|
||||
|
||||
# OVERRIDE: Custom search filter with trigram search
|
||||
if config.get("search"):
|
||||
search_fields = config.get("search")
|
||||
|
||||
@@ -209,6 +209,21 @@ async def test_get_book_file(
|
||||
assert downloaded_content == file_content
|
||||
|
||||
|
||||
async def test_list_books_by_id(populated_authenticated_client: AsyncClient) -> None:
|
||||
"""
|
||||
`?ids=` has to reach the database as integers.
|
||||
|
||||
advanced_alchemy's stock id filter annotates the parameter as `list[str]` whatever
|
||||
the configured id type, so the ids arrived as strings and Postgres refused to
|
||||
compare a bigint primary key against them. Nothing called it until a screen needed
|
||||
to fetch a handful of books by id.
|
||||
"""
|
||||
response = await populated_authenticated_client.get("/books?ids=1&ids=2&pageSize=10")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert sorted(book["id"] for book in response.json()["items"]) == [1, 2]
|
||||
|
||||
|
||||
async def test_get_book_by_id(populated_authenticated_client: AsyncClient) -> None:
|
||||
"""Test retrieving a specific book by ID."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user