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.
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"}}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With