Using pydantic setting management, how can I load env variables on nested setting objects on a main settings class? In the code below, the sub_field
env variable field doesn't get loaded. field_one
and field_two
load fine. How can I load an environment file so the values are propagated down to the nested sub_settings
object?
from typing import Optional
from pydantic import BaseSettings, Field
class SubSettings(BaseSettings):
sub_field: Optional[str] = Field(None, env='SUB_FIELD')
class Settings(BaseSettings):
field_one: Optional[str] = Field(None, env='FIELD_ONE')
field_two: Optional[int] = Field(None, env='FIELD_TWO')
sub_settings: SubSettings = SubSettings()
settings = Settings(_env_file='local.env')
There are some examples of nested loading of pydantic env variables in the docs.
If you're willing to adjust your variable names, one strategy is to use env_nested_delimiter
to denote nested fields. This appears to be the way that pydantic expects nested settings to be loaded, so it should be preferred when possible.
So with a local.env like this:
FIELD_ONE=one
FIELD_TWO=2
SUB_SETTINGS__SUB_FIELD=value
You should be able to load the settings in this way
from typing import Optional
from pydantic import BaseModel, BaseSettings
class SubSettings(BaseModel):
# ^ Note that this inherits from BaseModel, not BaseSettings
sub_field: Optional[str]
class Settings(BaseSettings):
field_one: Optional[str]
field_two: Optional[int]
sub_settings: SubSettings
class Config:
env_nested_delimiter = '__'
If you don't want to use the env_nested_delimiter
functionality, you could load both sets of settings from the same local.env
file. Then pass the loaded SubSettings to Settings directly. This can be done by overriding the Settings
class __init__
method
With this local.env
FIELD_ONE=one
FIELD_TWO=2
SUB_FIELD=value
use the following to load the settings
from typing import Optional
from pydantic import BaseModel, BaseSettings, Field
class SubSettings(BaseSettings):
sub_field: Optional[str]
class Settings(BaseSettings):
field_one: Optional[str]
field_two: Optional[int]
sub_settings: SubSettings
def __init__(self, *args, **kwargs):
kwargs['sub_settings'] = SubSettings(_env_file=kwargs['_env_file'])
super().__init__(*args, **kwargs)
settings = Settings(_env_file='local.env')