Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested settings with pydantic-settings

Using pydantic-settings (v2.0.3), I want to manage settings for services service1 and service2, nesting them under the common settings object, so I can address them like settings.service1.secret1 or settings.service2.secret2. Currently, I have this code in my config.py:

from pydantic_settings import BaseSettings


class FirstServiceSettings(BaseSettings):
    secret1: str


class SecondServiceSettings(BaseSettings):
    secret2: str


class Settings(BaseSettings):
    service1: FirstServiceSettings
    service2: SecondServiceSettings

    class Config:
        env_file = ".env"


settings = Settings()

And this is my .env file:

secret1=secret1
secret2=secret2

However, I'm getting pydantic_core._pydantic_core.ValidationError: 4 validation errors for Settings.

The problem can be solved by adding service1__ and service2__ prefixes to keys in .env file and setting env_nested_delimiter = '__', but I wonder is there a way to make this without adding those prefixes in .env file.

like image 707
Vasily Avatar asked May 19 '26 20:05

Vasily


2 Answers

This problem can be solved using BaseModel and set extra='ignore' to BaseSettings:

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

print(f'{VERSION = }')
# > VERSION = '2.0.3'


class BaseServiceSettings(BaseSettings):
    model_config = SettingsConfigDict(env_file='.env', extra='ignore')


class FirstServiceSettings(BaseServiceSettings):
    secret1: str


class SecondServiceSettings(BaseServiceSettings):
    secret2: str


class Settings2(BaseModel):
    service1: FirstServiceSettings = FirstServiceSettings()
    service2: SecondServiceSettings = SecondServiceSettings()


settings = Settings2()
print(settings.model_dump_json())
# > {"service1":{"secret1":"secret1"},"service2":{"secret2":"secret2"}}
like image 193
Michael H Avatar answered May 22 '26 14:05

Michael H


2025:

There's now a pydantic-file-secrets package (disclaimer: authored by me) that allows to use secrets in nested settings models.

All you need is to import dedicated settings source FileSecretsSettingsSource and use it as the replacement for built-in SecretsSettingsSource.

For example, for directory layout

šŸ“‚ secrets
ā”œā”€ā”€ šŸ“„ app_key
└── šŸ“„ db__passwd

and settings model

class DbSettings(BaseModel):
    passwd: SecretStr


class Settings(BaseSettings):
    app_key: SecretStr
    db: DbSettings

the fully functional piece of code is

from pydantic import BaseModel, SecretStr
from pydantic_file_secrets import FileSecretsSettingsSource, SettingsConfigDict
from pydantic_settings import BaseSettings
from pydantic_settings.sources import PydanticBaseSettingsSource


class DbSettings(BaseModel):
    passwd: SecretStr


class Settings(BaseSettings):
    app_key: SecretStr
    db: DbSettings

    model_config = SettingsConfigDict(
        secrets_dir='secrets',
        secrets_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,
            FileSecretsSettingsSource(file_secret_settings),
        )

pydantic-file-secrets supports other directory layouts:

šŸ“‚ secrets
ā”œā”€ā”€ šŸ“„ app_key
└── šŸ“‚ db
    └── šŸ“„ passwd
šŸ“‚ secrets
ā”œā”€ā”€ šŸ“‚ layer1
│   └── šŸ“„ app_key
└── šŸ“‚ layer2
    └── šŸ“„ db__passwd

and has other useful features.

Please visit project repository for documentation and more examples.

like image 44
makukha Avatar answered May 22 '26 14:05

makukha



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!