Skip to content

Commit

Permalink
Fix nested model AliasChoices in validation alias (#411)
Browse files Browse the repository at this point in the history
  • Loading branch information
hramezani authored Sep 23, 2024
1 parent 9b73e92 commit fdd666b
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 9 deletions.
18 changes: 9 additions & 9 deletions pydantic_settings/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -778,8 +778,9 @@ def _field_is_complex(self, field: FieldInfo) -> tuple[bool, bool]:
# Default value of `case_sensitive` is `None`, because we don't want to break existing behavior.
# We have to change the method to a non-static method and use
# `self.case_sensitive` instead in V3.
@staticmethod
def next_field(field: FieldInfo | Any | None, key: str, case_sensitive: bool | None = None) -> FieldInfo | None:
def next_field(
self, field: FieldInfo | Any | None, key: str, case_sensitive: bool | None = None
) -> FieldInfo | None:
"""
Find the field in a sub model by key(env name)
Expand Down Expand Up @@ -815,21 +816,20 @@ class Cfg(BaseSettings):
annotation = field.annotation if isinstance(field, FieldInfo) else field
if origin_is_union(get_origin(annotation)) or isinstance(annotation, WithArgsTypes):
for type_ in get_args(annotation):
type_has_key = EnvSettingsSource.next_field(type_, key, case_sensitive)
type_has_key = self.next_field(type_, key, case_sensitive)
if type_has_key:
return type_has_key
elif is_model_class(annotation) or is_pydantic_dataclass(annotation):
fields = _get_model_fields(annotation)
# `case_sensitive is None` is here to be compatible with the old behavior.
# Has to be removed in V3.
for field_name, f in fields.items():
if case_sensitive is None or case_sensitive:
if (field_name == key) or (isinstance(f.validation_alias, str) and f.validation_alias == key):
for _, env_name, _ in self._extract_field_info(f, field_name):
if case_sensitive is None or case_sensitive:
if field_name == key or env_name == key:
return f
elif field_name.lower() == key.lower() or env_name.lower() == key.lower():
return f
elif (field_name.lower() == key.lower()) or (
isinstance(f.validation_alias, str) and f.validation_alias.lower() == key.lower()
):
return f
return None

def explode_env_vars(self, field_name: str, field: FieldInfo, env_vars: Mapping[str, str | None]) -> dict[str, Any]:
Expand Down
15 changes: 15 additions & 0 deletions tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5090,3 +5090,18 @@ class Settings(BaseSettings):
monkeypatch.setattr(os, 'environ', value={'nested__fooAlias': '["one", "two"]'})
s = Settings()
assert s.model_dump() == {'nested': {'foo': ['one', 'two']}}


def test_nested_model_field_with_alias_choices(env):
class NestedSettings(BaseModel):
foo: List[str] = Field(alias=AliasChoices('fooalias', 'foo-alias'))

class Settings(BaseSettings):
model_config = SettingsConfigDict(env_nested_delimiter='__')

nested: NestedSettings

env.set('nested__fooalias', '["one", "two"]')

s = Settings()
assert s.model_dump() == {'nested': {'foo': ['one', 'two']}}

0 comments on commit fdd666b

Please sign in to comment.