Skip to content

Commit 9dc0ca2

Browse files
authored
Fix issue with pydantic v2 params validation (#781)
1 parent 3226a1b commit 9dc0ca2

File tree

2 files changed

+32
-3
lines changed

2 files changed

+32
-3
lines changed

fastapi_pagination/api.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@
3737
lenient_issubclass,
3838
)
3939
from fastapi.routing import APIRoute, APIRouter
40+
from pydantic import BaseModel
4041

4142
from .bases import AbstractPage, AbstractParams
4243
from .default import Page
4344
from .types import AsyncItemsTransformer, ItemsTransformer, SyncItemsTransformer
44-
from .utils import is_async_callable
45+
from .utils import IS_PYDANTIC_V2, is_async_callable
4546

4647
T = TypeVar("T")
4748
TAbstractParams = TypeVar("TAbstractParams", covariant=True, bound=AbstractParams)
@@ -245,7 +246,18 @@ async def _pagination_params(*args: Any, **kwargs: Any) -> AsyncIterator[TAbstra
245246
with _ctx_var_with_reset(_params_val, cast(AbstractParams, val)):
246247
yield val
247248

248-
_pagination_params.__signature__ = inspect.signature(params) # type: ignore[attr-defined]
249+
sign = inspect.signature(params)
250+
251+
if IS_PYDANTIC_V2:
252+
with suppress(ValueError, TypeError):
253+
if issubclass(params, BaseModel):
254+
sign_params = {**sign.parameters}
255+
for name, field in params.model_fields.items():
256+
sign_params[name] = sign_params[name].replace(default=field)
257+
258+
sign = sign.replace(parameters=[*sign_params.values()])
259+
260+
_pagination_params.__signature__ = sign # type: ignore[attr-defined]
249261

250262
return _pagination_params
251263

tests/test_api.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Any, Generic, Sequence, TypeVar
22

3-
from fastapi import Depends, FastAPI, Request, Response
3+
from fastapi import Depends, FastAPI, Request, Response, status
44
from fastapi.routing import APIRouter
55
from fastapi.testclient import TestClient
66
from pydantic import Field
@@ -235,3 +235,20 @@ async def async_transformer(items):
235235
match=r"^apply_items_transformer called with async_=False but transformer is async$",
236236
):
237237
apply_items_transformer([], async_transformer, async_=False)
238+
239+
240+
def test_no_exception_on_validation_error():
241+
app = FastAPI()
242+
client = TestClient(app)
243+
244+
@app.get(
245+
"/",
246+
response_model=Page[int],
247+
)
248+
async def route():
249+
return paginate([])
250+
251+
add_pagination(app)
252+
253+
rsp = client.get("/", params={"page": -1})
254+
assert rsp.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY

0 commit comments

Comments
 (0)