Skip to content

Commit

Permalink
Use generated alias for aliases that are not specified otherwise (#7802)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexmojaki committed Oct 16, 2023
1 parent c75cecd commit d787222
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 4 deletions.
16 changes: 12 additions & 4 deletions pydantic/_internal/_generate_schema.py
Expand Up @@ -996,14 +996,22 @@ def json_schema_update_func(schema: CoreSchemaOrField, handler: GetJsonSchemaHan

# apply alias generator
alias_generator = self._config_wrapper.alias_generator
if alias_generator and (field_info.alias_priority is None or field_info.alias_priority <= 1):
if alias_generator and (
field_info.alias_priority is None or field_info.alias_priority <= 1 or field_info.alias is None
):
alias = alias_generator(name)
if not isinstance(alias, str):
raise TypeError(f'alias_generator {alias_generator} must return str, not {alias.__class__}')
if field_info.alias is None:
if field_info.serialization_alias is None:
field_info.serialization_alias = alias
if field_info.validation_alias is None:
field_info.validation_alias = alias
else:
field_info.serialization_alias = alias
field_info.validation_alias = alias
field_info.alias_priority = 1
field_info.alias = alias
field_info.validation_alias = alias
field_info.serialization_alias = alias
field_info.alias_priority = 1

if isinstance(field_info.validation_alias, (AliasChoices, AliasPath)):
validation_alias = field_info.validation_alias.convert_to_aliases()
Expand Down
58 changes: 58 additions & 0 deletions tests/test_aliases.py
Expand Up @@ -216,6 +216,64 @@ class Child(Parent):
assert [f.alias for f in Child.model_fields.values()] == ['abc', 'Y', 'Z']


def test_alias_generator_used_by_default():
class Model(BaseModel):
model_config = ConfigDict(alias_generator=lambda x: x.upper())

a: str
b: str = Field(..., alias='b_alias')
c: str = Field(..., validation_alias='c_val_alias')
d: str = Field(..., serialization_alias='d_ser_alias')
e: str = Field(..., alias='e_alias', validation_alias='e_val_alias')
f: str = Field(..., alias='f_alias', serialization_alias='f_ser_alias')
g: str = Field(..., alias='g_alias', validation_alias='g_val_alias', serialization_alias='g_ser_alias')

assert {
name: {k: getattr(f, k) for k in ('alias', 'validation_alias', 'serialization_alias')}
for name, f in Model.model_fields.items()
} == {
# Validation/serialization aliases should be:
# 1. The specific alias, if specified, or
# 2. The alias, if specified, or
# 3. The generated alias (i.e. the field name in upper case)
'a': {
'alias': 'A',
'validation_alias': 'A',
'serialization_alias': 'A',
},
'b': {
'alias': 'b_alias',
'validation_alias': 'b_alias',
'serialization_alias': 'b_alias',
},
'c': {
'alias': 'C',
'validation_alias': 'c_val_alias',
'serialization_alias': 'C',
},
'd': {
'alias': 'D',
'validation_alias': 'D',
'serialization_alias': 'd_ser_alias',
},
'e': {
'alias': 'e_alias',
'validation_alias': 'e_val_alias',
'serialization_alias': 'e_alias',
},
'f': {
'alias': 'f_alias',
'validation_alias': 'f_alias',
'serialization_alias': 'f_ser_alias',
},
'g': {
'alias': 'g_alias',
'validation_alias': 'g_val_alias',
'serialization_alias': 'g_ser_alias',
},
}


def test_low_priority_alias():
class Parent(BaseModel):
w: bool = Field(..., alias='w_', validation_alias='w_val_alias', serialization_alias='w_ser_alias')
Expand Down

0 comments on commit d787222

Please sign in to comment.