2

Let's say I have 2 simple BaseSettings classes that, each, load values from their individual environment files. Let's also say that there is a combined settings class that is based on the first 2 classes.

Settings = DatabaseSettigns + AuthSettings

How can I create the combined settings class with a flat namespace without having to do dynamic class creation or dynamic attribute value assignments, i.e., I want my IDE to be able to do auto-complete, etc by knowing what the attributes are.

When I use the example below, I ONLY get the values from the LAST class, i.e, AuthSettings, instead of BOTH of them.

How can I have it such that ALL component settings classes are properly read / initialized from their respective files?

import logging
log = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO,
                    format='%(message)s')

from pydantic import BaseModel
from pydantic_settings import BaseSettings, SettingsConfigDict

class DatabaseSettings(BaseSettings):
  model_config = SettingsConfigDict(
    env_prefix="APP_",
    env_file=".env.database",
    env_file_encoding="utf-8",
    extra="ignore",
  )
  db_host: str = "localhost"

class AuthSettings(BaseSettings):
  model_config = SettingsConfigDict(
    env_prefix="APP_",
    env_file=".env.auth",
    env_file_encoding="utf-8",
    extra="ignore",
  )
  auth_secret_key: str = "change-me"

class Settings(DatabaseSettings, AuthSettings):
  model_config = SettingsConfigDict(
    env_prefix='APP_',
    extra='ignore'
  )
  pass

settings = Settings()
log.info(f"Settings: {settings}")

# .env.database
APP_DB_HOST=db.example.com
# .env.auth
APP_AUTH_SECRET_KEY=secret-from-env-file

Current Result

Settings: auth_secret_key='secret-from-env-file' db_host='localhost'

Desired Result

Settings: auth_secret_key='secret-from-env-file' db_host='db.example.com'
# notice how the db_host value is the one read from the file instead of the default "localhost"

2 Answers 2

3

You cannot automatically flatten multiple BaseSettings submodels into a single namespace in Pydantic while keeping composition pure (my previous answer). You can still use inheritance, but keep in mind that the model_config of the subclass (Settings) overwrites the configs of the parent classes. To fix your original problem, define all env_files in the final class using a list:

class Settings(DatabaseSettings, AuthSettings):
    model_config = SettingsConfigDict(
        env_prefix="APP_",
        env_file=[".env.database", ".env.auth"],
        env_file_encoding="utf-8",
        extra="ignore",
    )
New contributor
Mauricio Reisdoefer is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
Sign up to request clarification or add additional context in comments.

Comments

2

You can use composition instead of inheritance, which is more explicit, avoids conflicts between multiple BaseSettings classes, and preserves IDE auto-complete. It works like this:

class Settings(BaseModel):  # Doesn't inherit BaseSettings
    database: DatabaseSettings = DatabaseSettings()
    auth: AuthSettings = AuthSettings()

Why prefer composition?

  • Each settings class loads its own .env file correctly

  • No conflicts from multiple inheritance

  • Maintains clear structure and full IDE support

  • Easier to extend and maintain in the future

New contributor
Mauricio Reisdoefer is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

5 Comments

I have seen that answer before, but I would like to keep a flat namespace. Is that possible?
You cannot automatically flatten multiple BaseSettings submodels into a single namespace in Pydantic while keeping composition pure. You can still use inheritance, but keep in mind that the model_config of the subclass (Settings) overwrites the configs of the parent classes. To fix your original problem, define all env_files in the final class using a list: ``` class Settings(DatabaseSettings, AuthSettings): model_config = SettingsConfigDict( env_prefix="APP_", env_file=[".env.database", ".env.auth"], env_file_encoding="utf-8", extra="ignore",
THIS should be the correct answer. Would you mind posting it as an answer and I can mark it as the solution?
I personally recommend using composition, as it tends to be easier to manage in larger projects. Alternatively, you could consolidate all settings into a single class if that fits your use case better.
Is there any way to initialize one of the sub-settings models with a custom environment file? i.e., trying to do something like settings = Settings(_env_file=f".env.{app_env}.database") so that I can load DEV or TEST or PROD versions of the config values.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.