Pydantic nested setting objects load env variables from file

Question:

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')
Asked By: stormakt

||

Answers:

There are some examples of nested loading of pydantic env variables in the docs.

Option 1

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 = '__'

Alternate

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')
Answered By: cstoltze
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.