Browse Source

️ Improve performance in request body parsing with a cache for internal model fields (#12184)

pull/12189/head
Sebastián Ramírez 7 months ago
committed by GitHub
parent
commit
b0eedbb580
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 6
      fastapi/_compat.py
  2. 4
      fastapi/dependencies/utils.py
  3. 16
      tests/test_compat.py

6
fastapi/_compat.py

@ -2,6 +2,7 @@ from collections import deque
from copy import copy
from dataclasses import dataclass, is_dataclass
from enum import Enum
from functools import lru_cache
from typing import (
Any,
Callable,
@ -649,3 +650,8 @@ def is_uploadfile_sequence_annotation(annotation: Any) -> bool:
is_uploadfile_or_nonable_uploadfile_annotation(sub_annotation)
for sub_annotation in get_args(annotation)
)
@lru_cache
def get_cached_model_fields(model: Type[BaseModel]) -> List[ModelField]:
return get_model_fields(model)

4
fastapi/dependencies/utils.py

@ -32,8 +32,8 @@ from fastapi._compat import (
evaluate_forwardref,
field_annotation_is_scalar,
get_annotation_from_field_info,
get_cached_model_fields,
get_missing_field_error,
get_model_fields,
is_bytes_field,
is_bytes_sequence_field,
is_scalar_field,
@ -810,7 +810,7 @@ async def request_body_to_args(
fields_to_extract: List[ModelField] = body_fields
if single_not_embedded_field and lenient_issubclass(first_field.type_, BaseModel):
fields_to_extract = get_model_fields(first_field.type_)
fields_to_extract = get_cached_model_fields(first_field.type_)
if isinstance(received_body, FormData):
body_to_process = await _extract_form_body(fields_to_extract, received_body)

16
tests/test_compat.py

@ -5,6 +5,7 @@ from fastapi._compat import (
ModelField,
Undefined,
_get_model_config,
get_cached_model_fields,
get_model_fields,
is_bytes_sequence_annotation,
is_scalar_field,
@ -102,3 +103,18 @@ def test_is_pv1_scalar_field():
fields = get_model_fields(Model)
assert not is_scalar_field(fields[0])
def test_get_model_fields_cached():
class Model(BaseModel):
foo: str
non_cached_fields = get_model_fields(Model)
non_cached_fields2 = get_model_fields(Model)
cached_fields = get_cached_model_fields(Model)
cached_fields2 = get_cached_model_fields(Model)
for f1, f2 in zip(cached_fields, cached_fields2):
assert f1 is f2
assert non_cached_fields is not non_cached_fields2
assert cached_fields is cached_fields2

Loading…
Cancel
Save