mirror of
https://github.com/fastapi/fastapi.git
synced 2026-09-09 20:07:23 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcd4e895e1 | ||
|
|
27c922c403 | ||
|
|
cc96fd3a7c | ||
|
|
ac068db630 | ||
|
|
6be87d13a7 | ||
|
|
23e4a10434 |
No files matched your search
@@ -7,6 +7,18 @@ hide:
|
||||
|
||||
## Latest Changes
|
||||
|
||||
## 0.140.6 (2026-07-27)
|
||||
|
||||
### Refactors
|
||||
|
||||
* ⚡️ Avoid flattening dependencies for request parameters, mainly for OpenAPI. PR [#16073](https://github.com/fastapi/fastapi/pull/16073) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.140.5 (2026-07-27)
|
||||
|
||||
### Refactors
|
||||
|
||||
* ⚡️ Avoid flattening dependencies for body fields. PR [#16071](https://github.com/fastapi/fastapi/pull/16071) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.140.4 (2026-07-27)
|
||||
|
||||
### Refactors
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
|
||||
|
||||
__version__ = "0.140.4"
|
||||
__version__ = "0.140.6"
|
||||
|
||||
from starlette import status as status
|
||||
|
||||
|
||||
@@ -219,6 +219,16 @@ def get_flat_dependant(
|
||||
return flat_dependant
|
||||
|
||||
|
||||
def _get_flat_body_params(dependant: Dependant) -> list[ModelField]:
|
||||
body_params: list[ModelField] = []
|
||||
dependants = [dependant]
|
||||
while dependants:
|
||||
current_dependant = dependants.pop()
|
||||
body_params.extend(current_dependant.body_params)
|
||||
dependants.extend(reversed(current_dependant.dependencies))
|
||||
return body_params
|
||||
|
||||
|
||||
def _get_flat_fields_from_params(fields: list[ModelField]) -> list[ModelField]:
|
||||
if not fields:
|
||||
return fields
|
||||
@@ -232,11 +242,31 @@ def _get_flat_fields_from_params(fields: list[ModelField]) -> list[ModelField]:
|
||||
|
||||
|
||||
def get_flat_params(dependant: Dependant) -> list[ModelField]:
|
||||
flat_dependant = get_flat_dependant(dependant, skip_repeats=True)
|
||||
path_params = _get_flat_fields_from_params(flat_dependant.path_params)
|
||||
query_params = _get_flat_fields_from_params(flat_dependant.query_params)
|
||||
header_params = _get_flat_fields_from_params(flat_dependant.header_params)
|
||||
cookie_params = _get_flat_fields_from_params(flat_dependant.cookie_params)
|
||||
path_params: list[ModelField] = []
|
||||
query_params: list[ModelField] = []
|
||||
header_params: list[ModelField] = []
|
||||
cookie_params: list[ModelField] = []
|
||||
visited: list[DependencyCacheKey] = []
|
||||
uses_scopes_cache: _UsesScopesCache = {}
|
||||
dependants = [dependant]
|
||||
while dependants:
|
||||
current_dependant = dependants.pop()
|
||||
cache_key = _get_cache_key(
|
||||
dependant=current_dependant,
|
||||
uses_scopes_cache=uses_scopes_cache,
|
||||
)
|
||||
if cache_key in visited:
|
||||
continue
|
||||
visited.append(cache_key)
|
||||
path_params.extend(current_dependant.path_params)
|
||||
query_params.extend(current_dependant.query_params)
|
||||
header_params.extend(current_dependant.header_params)
|
||||
cookie_params.extend(current_dependant.cookie_params)
|
||||
dependants.extend(reversed(current_dependant.dependencies))
|
||||
path_params = _get_flat_fields_from_params(path_params)
|
||||
query_params = _get_flat_fields_from_params(query_params)
|
||||
header_params = _get_flat_fields_from_params(header_params)
|
||||
cookie_params = _get_flat_fields_from_params(cookie_params)
|
||||
return path_params + query_params + header_params + cookie_params
|
||||
|
||||
|
||||
@@ -1043,8 +1073,8 @@ async def request_body_to_args(
|
||||
return values, errors
|
||||
|
||||
|
||||
def get_body_field(
|
||||
*, flat_dependant: Dependant, name: str, embed_body_fields: bool
|
||||
def _get_body_field(
|
||||
*, body_params: list[ModelField], name: str, embed_body_fields: bool
|
||||
) -> ModelField | None:
|
||||
"""
|
||||
Get a ModelField representing the request body for a path operation, combining
|
||||
@@ -1056,34 +1086,30 @@ def get_body_field(
|
||||
This is **not** used to validate/parse the request body, that's done with each
|
||||
individual body parameter.
|
||||
"""
|
||||
if not flat_dependant.body_params:
|
||||
if not body_params:
|
||||
return None
|
||||
first_param = flat_dependant.body_params[0]
|
||||
first_param = body_params[0]
|
||||
if not embed_body_fields:
|
||||
return first_param
|
||||
model_name = "Body_" + name
|
||||
BodyModel = create_body_model(
|
||||
fields=flat_dependant.body_params, model_name=model_name
|
||||
)
|
||||
required = any(
|
||||
True for f in flat_dependant.body_params if f.field_info.is_required()
|
||||
)
|
||||
BodyModel = create_body_model(fields=body_params, model_name=model_name)
|
||||
required = any(True for f in body_params if f.field_info.is_required())
|
||||
BodyFieldInfo_kwargs: dict[str, Any] = {
|
||||
"annotation": BodyModel,
|
||||
"alias": "body",
|
||||
}
|
||||
if not required:
|
||||
BodyFieldInfo_kwargs["default"] = None
|
||||
if any(isinstance(f.field_info, params.File) for f in flat_dependant.body_params):
|
||||
if any(isinstance(f.field_info, params.File) for f in body_params):
|
||||
BodyFieldInfo: type[params.Body] = params.File
|
||||
elif any(isinstance(f.field_info, params.Form) for f in flat_dependant.body_params):
|
||||
elif any(isinstance(f.field_info, params.Form) for f in body_params):
|
||||
BodyFieldInfo = params.Form
|
||||
else:
|
||||
BodyFieldInfo = params.Body
|
||||
|
||||
body_param_media_types = [
|
||||
f.field_info.media_type
|
||||
for f in flat_dependant.body_params
|
||||
for f in body_params
|
||||
if isinstance(f.field_info, params.Body)
|
||||
]
|
||||
if len(set(body_param_media_types)) == 1:
|
||||
|
||||
+9
-9
@@ -55,10 +55,10 @@ from fastapi.dependencies.models import (
|
||||
_is_gen_callable,
|
||||
)
|
||||
from fastapi.dependencies.utils import (
|
||||
_get_body_field,
|
||||
_get_flat_body_params,
|
||||
_should_embed_body_fields,
|
||||
get_body_field,
|
||||
get_dependant,
|
||||
get_flat_dependant,
|
||||
get_parameterless_sub_dependant,
|
||||
get_stream_item_type,
|
||||
get_typed_return_annotation,
|
||||
@@ -849,16 +849,16 @@ def _build_dependant_with_parameterless_dependencies(
|
||||
path: str,
|
||||
call: Callable[..., Any],
|
||||
dependencies: Sequence[params.Depends],
|
||||
) -> tuple[Dependant, Dependant, bool]:
|
||||
) -> tuple[Dependant, list[ModelField], bool]:
|
||||
dependant = get_dependant(path=path, call=call, scope="function")
|
||||
for depends in dependencies[::-1]:
|
||||
dependant.dependencies.insert(
|
||||
0,
|
||||
get_parameterless_sub_dependant(depends=depends, path=path),
|
||||
)
|
||||
flat_dependant = get_flat_dependant(dependant)
|
||||
embed_body_fields = _should_embed_body_fields(flat_dependant.body_params)
|
||||
return dependant, flat_dependant, embed_body_fields
|
||||
body_params = _get_flat_body_params(dependant)
|
||||
embed_body_fields = _should_embed_body_fields(body_params)
|
||||
return dependant, body_params, embed_body_fields
|
||||
|
||||
|
||||
class _RouteWithPath(Protocol):
|
||||
@@ -1090,15 +1090,15 @@ def _populate_api_route_state(
|
||||
assert callable(endpoint), "An endpoint must be a callable"
|
||||
(
|
||||
route.dependant,
|
||||
flat_dependant,
|
||||
body_params,
|
||||
route._embed_body_fields,
|
||||
) = _build_dependant_with_parameterless_dependencies(
|
||||
path=route.path_format,
|
||||
call=route.endpoint,
|
||||
dependencies=route.dependencies,
|
||||
)
|
||||
route.body_field = get_body_field(
|
||||
flat_dependant=flat_dependant,
|
||||
route.body_field = _get_body_field(
|
||||
body_params=body_params,
|
||||
name=route.unique_id,
|
||||
embed_body_fields=route._embed_body_fields,
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user