Browse Source

Add `validation_alias` and `serialization_alias` properties to `ModelField`

pull/14371/head
Yurii Motov 8 months ago
parent
commit
e2566d7144
  1. 20
      fastapi/_compat/v2.py
  2. 25
      fastapi/dependencies/utils.py
  3. 5
      fastapi/openapi/utils.py

20
fastapi/_compat/v2.py

@ -70,6 +70,18 @@ class ModelField:
a = self.field_info.alias a = self.field_info.alias
return a if a is not None else self.name return a if a is not None else self.name
@property
def validation_alias(self) -> Union[str, None]:
va = self.field_info.validation_alias
if isinstance(va, str) and va:
return va
return None
@property
def serialization_alias(self) -> Union[str, None]:
sa = self.field_info.serialization_alias
return sa or None
@property @property
def required(self) -> bool: def required(self) -> bool:
return self.field_info.is_required() return self.field_info.is_required()
@ -183,12 +195,18 @@ def get_schema_from_model_field(
override_mode: Union[Literal["validation"], None] = ( override_mode: Union[Literal["validation"], None] = (
None if separate_input_output_schemas else "validation" None if separate_input_output_schemas else "validation"
) )
field_alias = (
(field.validation_alias or field.alias)
if field.mode == "validation"
else (field.serialization_alias or field.alias)
)
# This expects that GenerateJsonSchema was already used to generate the definitions # This expects that GenerateJsonSchema was already used to generate the definitions
json_schema = field_mapping[(field, override_mode or field.mode)] json_schema = field_mapping[(field, override_mode or field.mode)]
if "$ref" not in json_schema: if "$ref" not in json_schema:
# TODO remove when deprecating Pydantic v1 # TODO remove when deprecating Pydantic v1
# Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207 # Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207
json_schema["title"] = field.field_info.title or field.alias.title().replace( json_schema["title"] = field.field_info.title or field_alias.title().replace(
"_", " " "_", " "
) )
return json_schema return json_schema

25
fastapi/dependencies/utils.py

@ -722,7 +722,7 @@ def _validate_value_with_model_field(
def _get_multidict_value( def _get_multidict_value(
field: ModelField, values: Mapping[str, Any], alias: Union[str, None] = None field: ModelField, values: Mapping[str, Any], alias: Union[str, None] = None
) -> Any: ) -> Any:
alias = alias or field.alias alias = alias or get_validation_alias(field)
if is_sequence_field(field) and isinstance(values, (ImmutableMultiDict, Headers)): if is_sequence_field(field) and isinstance(values, (ImmutableMultiDict, Headers)):
value = values.getlist(alias) value = values.getlist(alias)
else: else:
@ -779,15 +779,13 @@ def request_params_to_args(
field.field_info, "convert_underscores", default_convert_underscores field.field_info, "convert_underscores", default_convert_underscores
) )
if convert_underscores: if convert_underscores:
alias = ( alias = get_validation_alias(field)
field.alias if alias == field.name:
if field.alias != field.name alias = alias.replace("_", "-")
else field.name.replace("_", "-")
)
value = _get_multidict_value(field, received_params, alias=alias) value = _get_multidict_value(field, received_params, alias=alias)
if value is not None: if value is not None:
params_to_process[field.name] = value params_to_process[field.name] = value
processed_keys.add(alias or field.alias) processed_keys.add(alias or get_validation_alias(field))
processed_keys.add(field.name) processed_keys.add(field.name)
for key, value in received_params.items(): for key, value in received_params.items():
@ -811,7 +809,7 @@ def request_params_to_args(
assert isinstance(field_info, (params.Param, temp_pydantic_v1_params.Param)), ( assert isinstance(field_info, (params.Param, temp_pydantic_v1_params.Param)), (
"Params must be subclasses of Param" "Params must be subclasses of Param"
) )
loc = (field_info.in_.value, field.alias) loc = (field_info.in_.value, get_validation_alias(field))
v_, errors_ = _validate_value_with_model_field( v_, errors_ = _validate_value_with_model_field(
field=field, value=value, values=values, loc=loc field=field, value=value, values=values, loc=loc
) )
@ -900,7 +898,7 @@ async def _extract_form_body(
tg.start_soon(process_fn, sub_value.read) tg.start_soon(process_fn, sub_value.read)
value = serialize_sequence_value(field=field, value=results) value = serialize_sequence_value(field=field, value=results)
if value is not None: if value is not None:
values[field.alias] = value values[get_validation_alias(field)] = value
for key, value in received_body.items(): for key, value in received_body.items():
if key not in values: if key not in values:
values[key] = value values[key] = value
@ -938,11 +936,11 @@ async def request_body_to_args(
) )
return {first_field.name: v_}, errors_ return {first_field.name: v_}, errors_
for field in body_fields: for field in body_fields:
loc = ("body", field.alias) loc = ("body", get_validation_alias(field))
value: Optional[Any] = None value: Optional[Any] = None
if body_to_process is not None: if body_to_process is not None:
try: try:
value = body_to_process.get(field.alias) value = body_to_process.get(get_validation_alias(field))
# If the received body is a list, not a dict # If the received body is a list, not a dict
except AttributeError: except AttributeError:
errors.append(get_missing_field_error(loc)) errors.append(get_missing_field_error(loc))
@ -1021,3 +1019,8 @@ def get_body_field(
field_info=BodyFieldInfo(**BodyFieldInfo_kwargs), field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
) )
return final_field return final_field
def get_validation_alias(field: ModelField) -> str:
va = getattr(field, "validation_alias", None)
return va or field.alias

5
fastapi/openapi/utils.py

@ -19,6 +19,7 @@ from fastapi.dependencies.utils import (
_get_flat_fields_from_params, _get_flat_fields_from_params,
get_flat_dependant, get_flat_dependant,
get_flat_params, get_flat_params,
get_validation_alias,
) )
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX
@ -132,7 +133,7 @@ def _get_openapi_operation_parameters(
field_mapping=field_mapping, field_mapping=field_mapping,
separate_input_output_schemas=separate_input_output_schemas, separate_input_output_schemas=separate_input_output_schemas,
) )
name = param.alias name = get_validation_alias(param)
convert_underscores = getattr( convert_underscores = getattr(
param.field_info, param.field_info,
"convert_underscores", "convert_underscores",
@ -140,7 +141,7 @@ def _get_openapi_operation_parameters(
) )
if ( if (
param_type == ParamTypes.header param_type == ParamTypes.header
and param.alias == param.name and name == param.name
and convert_underscores and convert_underscores
): ):
name = param.name.replace("_", "-") name = param.name.replace("_", "-")

Loading…
Cancel
Save