Python配置管理完全指南:从dotenv到pydantic_settings
前言
嘿,朋友!👋
还在为Python项目的配置管理头疼吗?硬编码密码到处飞,换环境就要改代码,团队协作时配置五花八门...
别担心!今天我要带你玩转Python最火的配置管理方案:dotenv + pydantic_settings。看完这篇文章,你就能轻松管理任何规模的Python项目配置!
为什么需要配置管理?
传统方式的坑
还记得刚开始学Python时,我们经常这么写:
python
# config.py
DATABASE_HOST = "localhost"
DATABASE_PASSWORD = "123456" # 危险!密码直接写代码里
这种方式简直是灾难:
- ❌ 安全漏洞:密码、密钥全在代码里
- ❌ 环境切换痛苦:开发→测试→生产,每次都要改代码
- ❌ 团队协作混乱:每个人的配置都不一样
现代方案有多爽?
好的配置管理应该:
- ✅ 安全可靠:敏感信息藏得好好的
- ✅ 环境切换丝滑:一键切换开发/生产环境
- ✅ 类型安全:配置错了马上就知道
- ✅ 智能提示:IDE自动补全,爽到飞起
dotenv:环境变量管理神器
什么是dotenv?
简单说,dotenv就是个"环境变量搬运工"。它能把.env文件里的配置项"搬运"到系统的环境变量中,让Python程序轻松读取。
快速上手
先安装工具包:
bash
pip install python-dotenv
创建.env文件:
env
# .env
# 数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=your_secret_password
DB_NAME=myapp
# 应用配置
APP_DEBUG=true
APP_PORT=8000
在Python中使用:
python
import os
from dotenv import load_dotenv
# 加载.env文件
load_dotenv() # 自动找当前目录的.env文件
# 读取配置
db_host = os.getenv('DB_HOST')
db_port = os.getenv('DB_PORT')
app_debug = os.getenv('APP_DEBUG') == 'true'
print(f"数据库地址: {db_host}:{db_port}")
print(f"调试模式: {app_debug}")
多环境切换
实际项目中,我们经常需要切换环境:
python
from dotenv import load_dotenv
def load_env_config(env='dev'):
"""加载指定环境的配置"""
env_file = f'.env.{env}'
# 如果指定环境的文件不存在,就用默认的
if not os.path.exists(env_file):
env_file = '.env'
load_dotenv(env_file)
print(f"已加载配置: {env_file}")
# 使用示例
load_env_config('dev') # 开发环境
load_env_config('prod') # 生产环境
load_env_config('test') # 测试环境
pydantic_settings:配置管理升级版
为什么需要它?
dotenv虽然好用,但还有点小问题:
- 所有配置都是字符串类型
- 没有类型检查(端口配成"abc"也不会报错)
- 没有默认值支持
- IDE不给智能提示
pydantic_settings就是来解决这些问题的!
基础用法
安装依赖:
bash
pip install pydantic-settings
创建配置类:
python
from pydantic import Field
from pydantic_settings import BaseSettings
class DatabaseConfig(BaseSettings):
"""数据库配置类"""
# 基本配置
host: str = Field(default="localhost", description="数据库地址")
port: int = Field(default=3306, description="数据库端口")
username: str = Field(default="root", description="用户名")
password: str = Field(description="密码", min_length=8)
database: str = Field(description="数据库名")
# 连接池配置
pool_size: int = Field(default=10, ge=1, le=100, description="连接池大小")
pool_timeout: int = Field(default=30, ge=1, description="连接超时时间")
class Config:
env_prefix = "DB_" # 环境变量前缀
env_file = ".env" # 自动加载的.env文件
# 使用配置
db_config = DatabaseConfig()
print(f"连接数据库: {db_config.host}:{db_config.port}")
print(f"连接池大小: {db_config.pool_size}")
配置自动映射
pydantic_settings会自动匹配环境变量:
python
class Settings(BaseSettings):
db_host: str # 对应 DB_HOST
DB_PORT: int # 对应 DB_PORT(大小写不敏感)
DbName: str # 对应 DB_NAME(自动转下划线)
类型验证和智能提示
python
from pydantic import validator
class AppSettings(BaseSettings):
app_name: str = Field(min_length=2, max_length=50)
debug: bool = False
port: int = Field(ge=1024, le=65535) # 端口范围限制
@validator('app_name')
def validate_app_name(cls, v):
if ' ' in v:
raise ValueError('应用名不能有空格')
return v.title()
实战:搭建完整配置系统
项目结构
myapp/
├── config/
│ ├── __init__.py
│ ├── settings.py # 配置类
│ └── database.py # 数据库连接
├── .env # 默认配置
├── .env.dev # 开发环境
├── .env.prod # 生产环境
└── main.py # 主程序
环境配置文件
.env.dev(开发环境):
env
# 应用配置
APP_NAME=我的小应用
APP_DEBUG=true
APP_PORT=8000
# 数据库配置
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=dev_password_here
DB_NAME=myapp_dev
DB_POOL_SIZE=10
# Redis配置
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
.env.prod(生产环境):
env
# 应用配置
APP_NAME=我的小应用-生产版
APP_DEBUG=false
APP_PORT=80
# 数据库配置
DB_HOST=prod-db.example.com
DB_PORT=5432
DB_USER=app_user
DB_PASSWORD=strong_prod_password
DB_NAME=myapp_prod
DB_POOL_SIZE=50
# Redis配置
REDIS_HOST=redis.example.com
REDIS_PORT=6379
REDIS_PASSWORD=redis_password
REDIS_DB=1
配置管理器
config/settings.py:
python
import os
from functools import lru_cache
from typing import Literal
from dotenv import load_dotenv, find_dotenv
from pydantic import Field, validator
from pydantic_settings import BaseSettings
class AppSettings(BaseSettings):
"""应用配置"""
app_name: str = Field(min_length=2, description="应用名称")
debug: bool = Field(default=False, description="调试模式")
port: int = Field(default=8000, ge=1024, le=65535, description="应用端口")
env: str = Field(default="dev", description="运行环境")
@validator('env')
def validate_env(cls, v):
allowed = ['dev', 'test', 'prod']
if v not in allowed:
raise ValueError(f'环境必须是: {allowed}')
return v
class DatabaseSettings(BaseSettings):
"""数据库配置"""
host: str = Field(default="localhost", description="数据库地址")
port: int = Field(default=5432, description="数据库端口")
username: str = Field(default="postgres", description="用户名")
password: str = Field(min_length=8, description="密码")
database: str = Field(description="数据库名")
# 连接池配置
pool_size: int = Field(default=10, ge=1, le=100, description="连接池大小")
pool_timeout: int = Field(default=30, ge=1, description="连接超时时间")
pool_recycle: int = Field(default=3600, ge=1, description="连接回收时间")
@property
def database_url(self) -> str:
"""自动构建数据库连接URL"""
return f"postgresql://{self.username}:{self.password}@{self.host}:{self.port}/{self.database}"
class Config:
env_prefix = "DB_"
class RedisSettings(BaseSettings):
"""Redis配置"""
host: str = Field(default="localhost", description="Redis地址")
port: int = Field(default=6379, description="Redis端口")
password: str = Field(default="", description="Redis密码")
database: int = Field(default=0, description="Redis数据库")
@property
def redis_url(self) -> str:
"""自动构建Redis连接URL"""
if self.password:
return f"redis://:{self.password}@{self.host}:{self.port}/{self.database}"
return f"redis://{self.host}:{self.port}/{self.database}"
class Config:
env_prefix = "REDIS_"
class ConfigManager:
"""配置管理器"""
def __init__(self):
self._load_env()
def _load_env(self):
"""智能加载环境文件"""
env = os.getenv('APP_ENV', 'dev')
env_file = f'.env.{env}'
# 自动查找环境文件
env_path = find_dotenv(env_file)
if not env_path:
env_path = find_dotenv('.env')
if env_path:
load_dotenv(env_path)
print(f"✅ 已加载配置: {env_path}")
else:
print("⚠️ 警告: 未找到 .env 文件")
@lru_cache()
def get_app_config(self) -> AppSettings:
"""获取应用配置(带缓存)"""
return AppSettings()
@lru_cache()
def get_database_config(self) -> DatabaseSettings:
"""获取数据库配置(带缓存)"""
return DatabaseSettings()
@lru_cache()
def get_redis_config(self) -> RedisSettings:
"""获取Redis配置(带缓存)"""
return RedisSettings()
# 创建全局配置实例
config = ConfigManager()
# 提供便捷访问
AppConfig = config.get_app_config()
DatabaseConfig = config.get_database_config()
RedisConfig = config.get_redis_config()
数据库连接配置
config/database.py:
python
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from config.settings import DatabaseConfig
# 获取配置
db_config = DatabaseConfig
# 创建数据库引擎
engine = create_engine(
db_config.database_url,
echo=db_config.debug, # 调试时打印SQL
pool_size=db_config.pool_size,
pool_timeout=db_config.pool_timeout,
pool_recycle=db_config.pool_recycle,
pool_pre_ping=True, # 连接前检查
)
# 创建会话工厂
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# 创建基类
Base = declarative_base()
# 数据库依赖
def get_db():
"""获取数据库会话"""
db = SessionLocal()
try:
yield db
finally:
db.close()
使用示例
python
from fastapi import FastAPI
from sqlalchemy.orm import Session
from config.settings import AppConfig, DatabaseConfig, RedisConfig
from config.database import engine, Base, get_db
# 创建FastAPI应用
app = FastAPI(
title=AppConfig.app_name,
debug=AppConfig.debug,
)
# 创建数据库表
Base.metadata.create_all(bind=engine)
@app.get("/")
def home():
"""首页信息"""
return {
"app_name": AppConfig.app_name,
"environment": AppConfig.env,
"debug": AppConfig.debug,
"database": DatabaseConfig.database,
"redis_host": RedisConfig.host,
}
@app.get("/db-info")
def db_info():
"""数据库配置信息"""
return {
"host": DatabaseConfig.host,
"port": DatabaseConfig.port,
"database": DatabaseConfig.database,
"pool_size": DatabaseConfig.pool_size,
"url": DatabaseConfig.database_url,
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app",
host="0.0.0.0",
port=AppConfig.port,
reload=AppConfig.debug,
)
实用技巧和最佳实践
🔒 安全第一
-
永远不要提交敏感文件
gitignore# .gitignore .env .env.* !.env.example -
提供配置模板
env# .env.example # 复制此文件为 .env 并填入你的配置 APP_NAME=你的应用名 APP_DEBUG=false DB_HOST=localhost DB_PORT=5432 DB_USER=your_username DB_PASSWORD=your_password DB_NAME=your_database
⚡ 性能优化
-
使用缓存避免重复加载
pythonfrom functools import lru_cache @lru_cache() def get_config(): return Settings() -
全局单例模式
python# 好的做法 config = get_config() # 避免重复创建 def func(): # config = Settings() # 不要这样做! pass
🐛 调试技巧
-
打印所有配置
pythondef debug_config(): config = DatabaseConfig() print("=== 数据库配置 ===") for key, value in config.dict().items(): print(f"{key}: {value}") -
验证配置有效性
pythondef check_config(): try: config = DatabaseConfig() print("✅ 配置验证通过") return True except Exception as e: print(f"❌ 配置验证失败: {e}") return False
总结
恭喜你!🎉 现在你已经掌握了Python配置管理的精髓:
- dotenv - 环境变量管理的基础
- pydantic_settings - 类型安全的配置管理
- 完整配置系统 - 多环境支持、模块化设计
- 最佳实践 - 安全、性能、调试技巧
这种配置管理方案的好处:
- 🔒 安全可靠 - 敏感信息得到保护
- 🔄 环境切换丝滑 - 一键切换开发/生产环境
- 💡 智能提示 - IDE自动补全,开发效率翻倍
- 🛡️ 类型安全 - 配置错误立即发现
- 🎯 易于维护 - 集中管理,清晰明了
记住,好的配置管理是项目成功的基础!希望这篇指南能帮助你在Python开发路上走得更远、更稳!
如果你觉得这篇文章对你有帮助,别忘了点个赞👍,也欢迎分享给其他小伙伴!
如果你有任何问题或建议,欢迎在评论区留言交流! 🚀
