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"
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