Skip to content

Commit fdd666b

Browse files
authored
Fix nested model AliasChoices in validation alias (#411)
1 parent 9b73e92 commit fdd666b

File tree

2 files changed

+24
-9
lines changed

2 files changed

+24
-9
lines changed

pydantic_settings/sources.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -778,8 +778,9 @@ def _field_is_complex(self, field: FieldInfo) -> tuple[bool, bool]:
778778
# Default value of `case_sensitive` is `None`, because we don't want to break existing behavior.
779779
# We have to change the method to a non-static method and use
780780
# `self.case_sensitive` instead in V3.
781-
@staticmethod
782-
def next_field(field: FieldInfo | Any | None, key: str, case_sensitive: bool | None = None) -> FieldInfo | None:
781+
def next_field(
782+
self, field: FieldInfo | Any | None, key: str, case_sensitive: bool | None = None
783+
) -> FieldInfo | None:
783784
"""
784785
Find the field in a sub model by key(env name)
785786
@@ -815,21 +816,20 @@ class Cfg(BaseSettings):
815816
annotation = field.annotation if isinstance(field, FieldInfo) else field
816817
if origin_is_union(get_origin(annotation)) or isinstance(annotation, WithArgsTypes):
817818
for type_ in get_args(annotation):
818-
type_has_key = EnvSettingsSource.next_field(type_, key, case_sensitive)
819+
type_has_key = self.next_field(type_, key, case_sensitive)
819820
if type_has_key:
820821
return type_has_key
821822
elif is_model_class(annotation) or is_pydantic_dataclass(annotation):
822823
fields = _get_model_fields(annotation)
823824
# `case_sensitive is None` is here to be compatible with the old behavior.
824825
# Has to be removed in V3.
825826
for field_name, f in fields.items():
826-
if case_sensitive is None or case_sensitive:
827-
if (field_name == key) or (isinstance(f.validation_alias, str) and f.validation_alias == key):
827+
for _, env_name, _ in self._extract_field_info(f, field_name):
828+
if case_sensitive is None or case_sensitive:
829+
if field_name == key or env_name == key:
830+
return f
831+
elif field_name.lower() == key.lower() or env_name.lower() == key.lower():
828832
return f
829-
elif (field_name.lower() == key.lower()) or (
830-
isinstance(f.validation_alias, str) and f.validation_alias.lower() == key.lower()
831-
):
832-
return f
833833
return None
834834

835835
def explode_env_vars(self, field_name: str, field: FieldInfo, env_vars: Mapping[str, str | None]) -> dict[str, Any]:

tests/test_settings.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5090,3 +5090,18 @@ class Settings(BaseSettings):
50905090
monkeypatch.setattr(os, 'environ', value={'nested__fooAlias': '["one", "two"]'})
50915091
s = Settings()
50925092
assert s.model_dump() == {'nested': {'foo': ['one', 'two']}}
5093+
5094+
5095+
def test_nested_model_field_with_alias_choices(env):
5096+
class NestedSettings(BaseModel):
5097+
foo: List[str] = Field(alias=AliasChoices('fooalias', 'foo-alias'))
5098+
5099+
class Settings(BaseSettings):
5100+
model_config = SettingsConfigDict(env_nested_delimiter='__')
5101+
5102+
nested: NestedSettings
5103+
5104+
env.set('nested__fooalias', '["one", "two"]')
5105+
5106+
s = Settings()
5107+
assert s.model_dump() == {'nested': {'foo': ['one', 'two']}}

0 commit comments

Comments
 (0)