mirror of https://github.com/tiangolo/fastapi.git
right directions maybe
This commit is contained in:
parent
d90b39a60a
commit
62344d272d
|
|
@ -1,6 +1,7 @@
|
|||
from typing import Annotated, Dict, List, Union
|
||||
|
||||
from fastapi import FastAPI, Query
|
||||
from pydantic import OnErrorOmit
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
|
@ -9,7 +10,7 @@ app = FastAPI()
|
|||
def get_mixed_mapping_mixed_type_query_params(
|
||||
query: Annotated[int, Query()] = None,
|
||||
mapping_query_str_or_int: Annotated[
|
||||
Union[Dict[str, str], Dict[str, int]], Query()
|
||||
Union[Dict[str, OnErrorOmit[str]], Dict[str, int]], Query()
|
||||
] = None,
|
||||
mapping_query_int: Annotated[Dict[str, int], Query()] = None,
|
||||
sequence_mapping_int: Annotated[Dict[str, List[int]], Query()] = None,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ from .main import get_compat_model_name_map as get_compat_model_name_map
|
|||
from .main import get_definitions as get_definitions
|
||||
from .main import get_missing_field_error as get_missing_field_error
|
||||
from .main import get_schema_from_model_field as get_schema_from_model_field
|
||||
from .main import ignore_invalid as ignore_invalid
|
||||
from .main import is_bytes_field as is_bytes_field
|
||||
from .main import is_bytes_sequence_field as is_bytes_sequence_field
|
||||
from .main import is_scalar_field as is_scalar_field
|
||||
|
|
@ -29,6 +28,7 @@ from .main import is_scalar_mapping_field as is_scalar_mapping_field
|
|||
from .main import is_scalar_sequence_field as is_scalar_sequence_field
|
||||
from .main import is_scalar_sequence_mapping_field as is_scalar_sequence_mapping_field
|
||||
from .main import is_sequence_field as is_sequence_field
|
||||
from .main import omit_by_default as omit_by_default
|
||||
from .main import serialize_sequence_value as serialize_sequence_value
|
||||
from .main import (
|
||||
with_info_plain_validator_function as with_info_plain_validator_function,
|
||||
|
|
@ -42,6 +42,9 @@ from .shared import PYDANTIC_V2 as PYDANTIC_V2
|
|||
from .shared import PYDANTIC_VERSION_MINOR_TUPLE as PYDANTIC_VERSION_MINOR_TUPLE
|
||||
from .shared import annotation_is_pydantic_v1 as annotation_is_pydantic_v1
|
||||
from .shared import field_annotation_is_scalar as field_annotation_is_scalar
|
||||
from .shared import (
|
||||
field_annotation_is_scalar_sequence_mapping as field_annotation_is_scalar_sequence_mapping,
|
||||
)
|
||||
from .shared import (
|
||||
is_uploadfile_or_nonable_uploadfile_annotation as is_uploadfile_or_nonable_uploadfile_annotation,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ if PYDANTIC_V2:
|
|||
from .v2 import Validator as Validator
|
||||
from .v2 import evaluate_forwardref as evaluate_forwardref
|
||||
from .v2 import get_missing_field_error as get_missing_field_error
|
||||
from .v2 import omit_by_default as omit_by_default
|
||||
from .v2 import (
|
||||
with_info_plain_validator_function as with_info_plain_validator_function,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ from collections import deque
|
|||
from dataclasses import is_dataclass
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Deque,
|
||||
FrozenSet,
|
||||
List,
|
||||
|
|
@ -19,7 +18,7 @@ from typing import (
|
|||
|
||||
from fastapi._compat import may_v1
|
||||
from fastapi.types import UnionType
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from pydantic import BaseModel
|
||||
from pydantic.version import VERSION as PYDANTIC_VERSION
|
||||
from starlette.datastructures import UploadFile
|
||||
from typing_extensions import Annotated, get_args, get_origin
|
||||
|
|
|
|||
|
|
@ -348,4 +348,3 @@ def create_body_model(
|
|||
|
||||
def get_model_fields(model: Type[BaseModel]) -> List[ModelField]:
|
||||
return list(model.__fields__.values()) # type: ignore[attr-defined]
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ from typing import (
|
|||
|
||||
from fastapi._compat import may_v1, shared
|
||||
from fastapi.openapi.constants import REF_TEMPLATE
|
||||
from fastapi.types import IncEx, ModelNameMap
|
||||
from pydantic import BaseModel, TypeAdapter, WrapValidator, create_model
|
||||
from fastapi.types import IncEx, ModelNameMap, UnionType
|
||||
from pydantic import BaseModel, OnErrorOmit, TypeAdapter, create_model
|
||||
from pydantic import PydanticSchemaGenerationError as PydanticSchemaGenerationError
|
||||
from pydantic import PydanticUndefinedAnnotation as PydanticUndefinedAnnotation
|
||||
from pydantic import ValidationError as ValidationError
|
||||
|
|
@ -488,3 +488,21 @@ def get_flat_models_from_fields(
|
|||
def get_long_model_name(model: TypeModelOrEnum) -> str:
|
||||
return f"{model.__module__}__{model.__qualname__}".replace(".", "__")
|
||||
|
||||
|
||||
def omit_by_default(annotation: Any) -> Any:
|
||||
# Update the annotation to use OnErrorOmit for the inner type(s)
|
||||
origin = get_origin(annotation)
|
||||
if origin is Union or origin is UnionType:
|
||||
new_args = []
|
||||
for arg in get_args(annotation):
|
||||
new_arg = omit_by_default(arg)
|
||||
new_args.append(new_arg)
|
||||
return Union[tuple(new_args)] # type: ignore[return-value]
|
||||
elif origin is Annotated:
|
||||
annotated_args = get_args(annotation)
|
||||
base_annotation = annotated_args[0]
|
||||
new_base_annotation = omit_by_default(base_annotation)
|
||||
new_metadata = annotated_args[1:]
|
||||
return Annotated[new_base_annotation + new_metadata] # type: ignore[return-value]
|
||||
else:
|
||||
return OnErrorOmit[annotation] # type: ignore[return-value]
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ from fastapi._compat import (
|
|||
create_body_model,
|
||||
evaluate_forwardref,
|
||||
field_annotation_is_scalar,
|
||||
field_annotation_is_scalar_sequence_mapping,
|
||||
get_annotation_from_field_info,
|
||||
get_cached_model_fields,
|
||||
get_missing_field_error,
|
||||
|
|
@ -45,6 +46,7 @@ from fastapi._compat import (
|
|||
is_uploadfile_sequence_annotation,
|
||||
lenient_issubclass,
|
||||
may_v1,
|
||||
omit_by_default,
|
||||
sequence_types,
|
||||
serialize_sequence_value,
|
||||
value_is_sequence,
|
||||
|
|
@ -63,7 +65,7 @@ from fastapi.security.oauth2 import OAuth2, SecurityScopes
|
|||
from fastapi.security.open_id_connect_url import OpenIdConnect
|
||||
from fastapi.types import DependencyCacheKey
|
||||
from fastapi.utils import create_model_field, get_path_param_names
|
||||
from pydantic import BaseModel, TypeAdapter
|
||||
from pydantic import BaseModel
|
||||
from pydantic.fields import FieldInfo
|
||||
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
|
||||
from starlette.concurrency import run_in_threadpool
|
||||
|
|
@ -485,6 +487,12 @@ def analyze_param(
|
|||
and getattr(field_info, "in_", None) is None
|
||||
):
|
||||
field_info.in_ = params.ParamTypes.query
|
||||
|
||||
if isinstance(
|
||||
field_info, (params.Query, temp_pydantic_v1_params.Query)
|
||||
) and field_annotation_is_scalar_sequence_mapping(use_annotation):
|
||||
use_annotation = omit_by_default(use_annotation)
|
||||
|
||||
use_annotation_from_field_info = get_annotation_from_field_info(
|
||||
use_annotation,
|
||||
field_info,
|
||||
|
|
@ -523,10 +531,6 @@ def analyze_param(
|
|||
and getattr(field, "shape", 1) == 1
|
||||
)
|
||||
)
|
||||
if is_scalar_sequence_field(field) or is_scalar_sequence_mapping_field(
|
||||
field
|
||||
):
|
||||
field._type_adapter.core_schema["on_error"] = "omit"
|
||||
|
||||
return ParamDetails(type_annotation=type_annotation, depends=depends, field=field)
|
||||
|
||||
|
|
|
|||
|
|
@ -305,6 +305,7 @@ class Query(Param): # type: ignore[misc]
|
|||
json_schema_extra=json_schema_extra,
|
||||
**extra,
|
||||
)
|
||||
self.annotation = self.annotation
|
||||
|
||||
|
||||
class Header(Param): # type: ignore[misc]
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ def test_foo_needy_very(client: TestClient):
|
|||
"query": 2,
|
||||
"mapping_query_str_or_int": {"foo": "baz"},
|
||||
"mapping_query_int": {},
|
||||
"sequence_mapping_int": {},
|
||||
"sequence_mapping_int": {"foo": []},
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue