|
14 | 14 | from fastapi import BackgroundTasks, HTTPException, params |
15 | 15 | from fastapi import Request as FastapiRequest |
16 | 16 | from fastapi import Response as FastapiResponse |
17 | | -from fastapi._compat import ModelField, _normalize_errors |
| 17 | +from fastapi._compat import ModelField |
18 | 18 | from fastapi.concurrency import run_in_threadpool |
19 | 19 | from fastapi.dependencies.models import Dependant |
20 | 20 | from fastapi.dependencies.utils import solve_dependencies |
21 | 21 | from fastapi.exceptions import RequestValidationError |
22 | 22 | from fastapi.responses import FileResponse, JSONResponse, StreamingResponse |
23 | | -from fastapi.routing import APIRoute, _prepare_response_content |
| 23 | +from fastapi.routing import APIRoute |
24 | 24 | from pydantic import BaseModel |
25 | 25 | from pydantic_core import PydanticUndefined |
26 | 26 | from starlette._utils import is_async_callable |
|
60 | 60 | ] |
61 | 61 |
|
62 | 62 |
|
63 | | -def _normalize_response_content( |
64 | | - content: Any, |
| 63 | +def _prepare_response_content( |
| 64 | + res: Any, |
65 | 65 | *, |
66 | | - exclude_unset: bool = False, |
| 66 | + exclude_unset: bool, |
67 | 67 | exclude_defaults: bool = False, |
68 | 68 | exclude_none: bool = False, |
69 | 69 | ) -> Any: |
70 | | - """Convert Pydantic models to dicts for FastAPI >= 0.126.0 compatibility. |
| 70 | + """Serialize Pydantic models to dicts for response processing. |
71 | 71 |
|
72 | | - In FastAPI >= 0.126.0, _prepare_response_content may return Pydantic model |
73 | | - instances instead of dicts. This function ensures the content is always |
74 | | - a dict/list/primitive for user migrations to work correctly. |
| 72 | + It is much easier to alter dicts and lists than Pydantic models in request/response migrations |
75 | 73 | """ |
76 | | - if isinstance(content, BaseModel): |
77 | | - return content.model_dump( |
| 74 | + if isinstance(res, BaseModel): |
| 75 | + return res.model_dump( |
78 | 76 | by_alias=True, |
79 | 77 | exclude_unset=exclude_unset, |
80 | 78 | exclude_defaults=exclude_defaults, |
81 | 79 | exclude_none=exclude_none, |
82 | 80 | ) |
83 | | - elif isinstance(content, list): |
| 81 | + elif isinstance(res, list): |
84 | 82 | return [ |
85 | | - _normalize_response_content( |
| 83 | + _prepare_response_content( |
86 | 84 | item, |
87 | 85 | exclude_unset=exclude_unset, |
88 | 86 | exclude_defaults=exclude_defaults, |
89 | 87 | exclude_none=exclude_none, |
90 | 88 | ) |
91 | | - for item in content |
| 89 | + for item in res |
92 | 90 | ] |
93 | | - elif isinstance(content, dict): |
| 91 | + elif isinstance(res, dict): |
94 | 92 | return { |
95 | | - k: _normalize_response_content( |
| 93 | + k: _prepare_response_content( |
96 | 94 | v, |
97 | 95 | exclude_unset=exclude_unset, |
98 | 96 | exclude_defaults=exclude_defaults, |
99 | 97 | exclude_none=exclude_none, |
100 | 98 | ) |
101 | | - for k, v in content.items() |
| 99 | + for k, v in res.items() |
102 | 100 | } |
103 | | - return content |
| 101 | + return res |
104 | 102 |
|
105 | 103 |
|
106 | 104 | APIVersionVarType: TypeAlias = Union[ContextVar[Union[VersionType, None]], ContextVar[VersionType]] |
@@ -445,9 +443,7 @@ async def _migrate_request( |
445 | 443 | background_tasks=background_tasks, |
446 | 444 | ) |
447 | 445 | if result.errors: |
448 | | - raise CadwynHeadRequestValidationError( |
449 | | - _normalize_errors(result.errors), body=request_info.body, version=current_version |
450 | | - ) |
| 446 | + raise CadwynHeadRequestValidationError(result.errors, body=request_info.body, version=current_version) |
451 | 447 | return result.values |
452 | 448 |
|
453 | 449 | def _migrate_response( |
@@ -610,16 +606,10 @@ async def _convert_endpoint_response_to_version( # noqa: C901 |
610 | 606 | else: |
611 | 607 | status_code = 200 |
612 | 608 | fastapi_response_dependency.status_code = status_code |
613 | | - prepared_content = _prepare_response_content( |
614 | | - response_or_response_body, |
615 | | - exclude_unset=head_route.response_model_exclude_unset, |
616 | | - exclude_defaults=head_route.response_model_exclude_defaults, |
617 | | - exclude_none=head_route.response_model_exclude_none, |
618 | | - ) |
619 | 609 | response_info = ResponseInfo( |
620 | 610 | fastapi_response_dependency, |
621 | | - _normalize_response_content( |
622 | | - prepared_content, |
| 611 | + _prepare_response_content( |
| 612 | + response_or_response_body, |
623 | 613 | exclude_unset=head_route.response_model_exclude_unset, |
624 | 614 | exclude_defaults=head_route.response_model_exclude_defaults, |
625 | 615 | exclude_none=head_route.response_model_exclude_none, |
|
0 commit comments