FastAPI 同一 Settings 类中加载多套环境配置的实现咨询
FastAPI + Pydantic 多环境动态加载.env配置方案
核心思路
先从优先级高于.env文件的来源(系统环境变量、服务启动参数、容器环境配置等)获取当前运行的环境标识,再根据标识拼接对应.env文件的路径,最后通过自定义配置源加载目标文件的配置,避免循环依赖问题。
Pydantic V2 实现代码
from pydantic_settings import BaseSettings, DotEnvSettingsSource from functools import lru_cache from typing import Tuple, Callable class APISettings(BaseSettings): # 环境标识,必须从系统环境变量/启动参数传入 environment: str # 业务配置字段 port: int = 8000 db_url: str debug: bool = False @classmethod def settings_customise_sources( cls, settings_cls: type[BaseSettings], init_settings: Callable, env_settings: Callable, dotenv_settings: Callable, file_secret_settings: Callable, ) -> Tuple[Callable, ...]: # 第一步:优先从初始化参数、系统环境变量读取环境标识 temp_config = init_settings(settings_cls) or env_settings(settings_cls) current_env = temp_config.get("environment") if not current_env or current_env not in ["dev", "stage", "prod"]: raise ValueError("请传入合法的环境标识:dev/stage/prod") # 第二步:拼接对应环境的配置文件路径 env_file_path = f"core/configs/.env.{current_env}" # 第三步:构造自定义的dotenv配置源 custom_dotenv = DotEnvSettingsSource( settings_cls, env_file=env_file_path, env_file_encoding="utf-8" ) # 配置源优先级:初始化参数 > 系统环境变量 > 自定义.env文件 > 机密文件 return (init_settings, env_settings, custom_dotenv, file_secret_settings) # 配置单例缓存 @lru_cache() def get_api_settings() -> APISettings: return APISettings()
Pydantic V1 实现代码
from pydantic import BaseSettings from pydantic.env_settings import SettingsSourceCallable from functools import lru_cache from typing import Tuple, Dict, Any from dotenv import dotenv_values class APISettings(BaseSettings): environment: str port: int = 8000 db_url: str debug: bool = False class Config: @classmethod def customise_sources( cls, init_settings: SettingsSourceCallable, env_settings: SettingsSourceCallable, file_secret_settings: SettingsSourceCallable, ) -> Tuple[SettingsSourceCallable, ...]: # 自定义配置源加载函数 def load_target_env_config(settings: BaseSettings) -> Dict[str, Any]: # 读取环境标识 current_env = init_settings(settings).get("environment") or env_settings(settings).get("environment") if not current_env or current_env not in ["dev", "stage", "prod"]: raise ValueError("请传入合法的环境标识:dev/stage/prod") # 加载对应.env文件 return dotenv_values(f"core/configs/.env.{current_env}", encoding="utf-8") # 调整配置加载优先级 return (init_settings, env_settings, load_target_env_config, file_secret_settings) @lru_cache() def get_api_settings() -> APISettings: return APISettings()
使用方法
启动服务时传入对应环境标识即可:
- Linux/Mac 命令:
ENVIRONMENT=prod uvicorn main:app --host 0.0.0.0 - Windows 命令:
set ENVIRONMENT=dev && uvicorn main:app --host 0.0.0.0 - Docker 启动:
docker run -e ENVIRONMENT=stage your-image-name
注意事项
environment字段必须从系统环境变量等高于.env的优先级来源传入,不可从目标.env文件读取,否则会出现循环依赖问题
- 本地开发如果不想每次手动传环境变量,可以在项目根目录新增一个不提交到代码库的
.env文件,仅写入environment=dev,在自定义配置源中增加该文件的加载逻辑即可 - 可以额外增加配置文件存在性校验,避免环境标识传错导致找不到配置文件的问题
内容的提问来源于stack exchange,提问作者mrgrj
相关产品推荐
相关产品推荐

