Skip to content

How to Override Deeply Nested Settings using Environment Variables? #203

Closed
@kschwab

Description

@kschwab

Hello,

Is it possible to override a deeply nested setting without having to redefine the entirety of the model?

Below is a modified example based off of Parsing environment variable values:

import os
from pydantic import BaseModel
from pydantic_settings import BaseSettings, SettingsConfigDict


class DeepSubModel(BaseModel):
    v4: str


class SubModel(BaseModel):
    v1: str
    v2: bytes
    v3: int
    deep: DeepSubModel


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

    v0: str
    sub_model: SubModel

    @classmethod
    def settings_customise_sources(
        cls,
        settings_cls,
        init_settings,
        env_settings,
        dotenv_settings,
        file_secret_settings):
        return env_settings, init_settings, file_secret_settings


# Ideal scenario would be a simple point modification
os.environ['SUB_MODEL__DEEP__V4'] = 'override-v4'

try:
    print(Settings(v0='0', sub_model=SubModel(v1='init-v1', v2=b'init-v2', v3=3, 
                   deep=DeepSubModel(v4='init-v4'))).model_dump())
except ValidationError as e:
    print(e)
    """
    pydantic_core._pydantic_core.ValidationError: 3 validation errors for Settings
    sub_model.v1
      Field required [type=missing, input_value={'deep': {'v4': 'override-v4'}}, input_type=dict]
        For further information visit https://errors.pydantic.dev/2.5/v/missing
    sub_model.v2
      Field required [type=missing, input_value={'deep': {'v4': 'override-v4'}}, input_type=dict]
        For further information visit https://errors.pydantic.dev/2.5/v/missing
    sub_model.v3
      Field required [type=missing, input_value={'deep': {'v4': 'override-v4'}}, input_type=dict]
        For further information visit https://errors.pydantic.dev/2.5/v/missing
    """

# Current scenario seems to require entire definition of nested modes etc.
os.environ['SUB_MODEL'] = '{"v1": "reinit-v1", "v2": "reinit-v2"}'
os.environ['SUB_MODEL__V3'] = '33'

print(Settings(v0='0', sub_model=SubModel(v1='init-v1', v2=b'init-v2', v3=3, 
               deep=DeepSubModel(v4='init-v4'))).model_dump())
"""
{'v0': '0', 'sub_model': {'v1': 'reinit-v1', 'v2': b'reinit-v2', 'v3': 33, 'deep': {'v4': 'override-v4'}}}
"""

The difference here is Settings is defined through instantiation instead of environment variables. Ideally, the below concept would still apply to Settings with respect to nested precedence, allowing for point modifications of nested variables:

Nested environment variables take precedence over the top-level environment variable JSON...

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions