dynaconf icon indicating copy to clipboard operation
dynaconf copied to clipboard

[RFC] Extend one enviroment with values from another

Open TjobberTjob opened this issue 8 months ago • 4 comments

Is your feature request related to a problem? Please describe. I find it annoying that i have to duplicate environmental settings or secrets, and i want to be able to daisy chain settings together and extend other environments

Describe the solution you'd like If i have multiple environments described in my settings or secrets.yaml files, i want to be able to extend other envirnments, via a key or similar.

default:
  ..

development:
  key: ab

local:
  _extends: development
  env: local

here settings.local.key should be "ab"

TjobberTjob avatar Jul 01 '25 12:07 TjobberTjob

We would love to see this! This is a requirement for our current project so we will need to hack something together. I wish it was natively supported in dynaconf.

aviau avatar Jul 28 '25 13:07 aviau

We would love to see this! This is a reuirement for our current project so we will need to hack something together. I with it was natively supported in dynaconf.

@aviau Did you manage to hack something together?

TjobberTjob avatar Aug 26 '25 05:08 TjobberTjob

Not directly on the file, but there is the way to set 2 environments at the same time.

https://www.dynaconf.com/configuration/#env

settings.yaml

default:
  server: localhost
development:
  server: dev.com
testing:
  port: 1111 

app.py

from dynaconf import Dynaconf
settings = Dynaconf(settings_files=["settings.yaml"], envvar_prefix="MYAPP", env_switcher="MYAPP_MODE")

print(f"server is {settings.server}")
print(f"port is {settings.port}")

Then

$ export MYAPP_MODE=development,testing
$ python app.py
server is dev.com   # comes from development
port is 1111        # comes from testing

rochacbruno avatar Aug 26 '25 13:08 rochacbruno

Did you manage to hack something together?

Yeah, it looks like this:

  • We parse ENV ourselves. It looks like staging-ci-potato where
    • staging is the base
    • ci is a layer
    • potato is another layer
  • Then we set SETTINGS_FILE_FOR_DYNACONF to staging.toml,ci.toml,potato.toml
  • Then we load overrides from the env
  • We also use pydantic to have a typed settings object

Pseudo-code:

def get_settings() -> PydanticModelForSettings:
    layers = parse_env(os.environ["ENV"]) # returns paths to toml files

    settings = LazySettings(
        SETTINGS_FILE_FOR_DYNACONF=",".join(layers),
        ENVIRONMENTS=False, # We set that to false, which means our toml files don't have per-env sections.
    )

    typed_settings = PydanticModelForSettings.model_validate(
        lazy_settings.to_dict()
    )

    return typed_settings

We also have unit tests that make sure that all our toml files are complete and dont have unkown variables:

def test_envs_are_valid():
    for env in ["staging", "prod", ...]:
        settings =  LazySettings(SETTINGS_FILE_FOR_DYNACONF=env+".toml", environments=False)
        typed_settings = PydanticModelForSettings.model_validate(settings.to_dict())

aviau avatar Aug 26 '25 14:08 aviau