pydantic / pydantic-settings

Settings management using pydantic
https://docs.pydantic.dev/latest/usage/pydantic_settings/
MIT License
508 stars 50 forks source link

How to override nested list of dictionaries #268

Closed asabellico closed 3 months ago

asabellico commented 3 months ago

Hello, i would like to override a value using environment vars, inside a config file of this kind:

instances:
  - id: "es1"
    url: "http://base_url:9200"
class InstanceConfig(BaseModel):
    id: str
    url: AnyUrl

class AppConfig(BaseConfig):
    instances: List[InstanceConfig]

    model_config = SettingsConfigDict(yaml_file="config.yaml", env_prefix="myproj_", env_nested_delimiter="__")

If I set this env var:

MYPROJ_INSTANCES__0__URL: "http://override_url:9200"

it gives me this error:

pydantic_core._pydantic_core.ValidationError: 1 validation error for AppConfig
instances
  Input should be a valid list [type=list_type, input_value={'0': {'url': 'http://override_url:9200'}}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.6/v/list_type

cannot find any reference about this in pydantic-settings documentation, so i wonder if this is supported or not

hramezani commented 3 months ago

yaml setting source is not enabled by default and you have to enable it like:

from typing import List, Tuple, Type

from pydantic import BaseModel, AnyUrl

from pydantic_settings import (
    BaseSettings,
    PydanticBaseSettingsSource,
    SettingsConfigDict,
    TomlConfigSettingsSource,
)

class InstanceConfig(BaseModel):
    id: str
    url: AnyUrl

class AppConfig(BaseSettings):
    instances: List[InstanceConfig]

    model_config = SettingsConfigDict(yaml_file="config.yaml", env_prefix="myproj_", env_nested_delimiter="__")

    @classmethod
    def settings_customise_sources(
        cls,
        settings_cls: Type[BaseSettings],
        init_settings: PydanticBaseSettingsSource,
        env_settings: PydanticBaseSettingsSource,
        dotenv_settings: PydanticBaseSettingsSource,
        file_secret_settings: PydanticBaseSettingsSource,
    ) -> Tuple[PydanticBaseSettingsSource, ...]:
        return init_settings, env_settings, dotenv_settings, file_secret_settings, TomlConfigSettingsSource(settings_cls)

Take a look at doc also helps

asabellico commented 3 months ago

you are right. i forgot to add the BaseConfig class from which i was inheriting that same thing (except I enabled yaml, env and secret sources). still i can't override a value in a dictionary in a list as described in the issue :(

asabellico commented 3 months ago

in fact i'm able to override a simple type config from env var but cannot do it correctly with data nested in a list using the 0 syntax in var name

hramezani commented 3 months ago

you can't override the item in a list. you can set it by setting the env lik: MYPROJ_INSTANCES='[{"id":"1", "url":"http://override_url:9200"}]'

asabellico commented 3 months ago

ok i was hoping it was possible even if not mentioned in the docs. thank you for the sanity check!