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

This commit is contained in:
Yurii Motov 2025-11-18 21:35:33 +01:00
parent 853b8c9c50
commit e2566d7144
3 changed files with 36 additions and 14 deletions

View File

@ -70,6 +70,18 @@ class ModelField:
a = self.field_info.alias
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
def required(self) -> bool:
return self.field_info.is_required()
@ -183,12 +195,18 @@ def get_schema_from_model_field(
override_mode: Union[Literal["validation"], None] = (
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
json_schema = field_mapping[(field, override_mode or field.mode)]
if "$ref" not in json_schema:
# TODO remove when deprecating Pydantic v1
# 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

View File

@ -722,7 +722,7 @@ def _validate_value_with_model_field(
def _get_multidict_value(
field: ModelField, values: Mapping[str, Any], alias: Union[str, None] = None
) -> Any:
alias = alias or field.alias
alias = alias or get_validation_alias(field)
if is_sequence_field(field) and isinstance(values, (ImmutableMultiDict, Headers)):
value = values.getlist(alias)
else:
@ -779,15 +779,13 @@ def request_params_to_args(
field.field_info, "convert_underscores", default_convert_underscores
)
if convert_underscores:
alias = (
field.alias
if field.alias != field.name
else field.name.replace("_", "-")
)
alias = get_validation_alias(field)
if alias == field.name:
alias = alias.replace("_", "-")
value = _get_multidict_value(field, received_params, alias=alias)
if value is not None:
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)
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)), (
"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(
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)
value = serialize_sequence_value(field=field, value=results)
if value is not None:
values[field.alias] = value
values[get_validation_alias(field)] = value
for key, value in received_body.items():
if key not in values:
values[key] = value
@ -938,11 +936,11 @@ async def request_body_to_args(
)
return {first_field.name: v_}, errors_
for field in body_fields:
loc = ("body", field.alias)
loc = ("body", get_validation_alias(field))
value: Optional[Any] = None
if body_to_process is not None:
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
except AttributeError:
errors.append(get_missing_field_error(loc))
@ -1021,3 +1019,8 @@ def get_body_field(
field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
)
return final_field
def get_validation_alias(field: ModelField) -> str:
va = getattr(field, "validation_alias", None)
return va or field.alias

View File

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