目錄
02-6. Settings 管理與環境變數
⏱️ 閱讀時間: 15 分鐘 🎯 難度: ⭐⭐ (基礎)
🤔 一句話解釋
pydantic-settings 讓你用型別安全的方式管理環境變數和設定檔。
🎯 為什麼需要設定管理?
常見問題
# ❌ 硬編碼
DATABASE_URL = "postgresql://user:password@localhost/db"
# ❌ 沒有驗證
import os
DATABASE_URL = os.getenv("DATABASE_URL") # 可能是 None
# ❌ 沒有型別
DEBUG = os.getenv("DEBUG") # 字串 "true",不是 bool使用 pydantic-settings
# ✅ 型別安全 + 自動驗證
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
debug: bool = False
settings = Settings() # 自動讀取環境變數📦 安裝
pip install pydantic-settings🔧 基本用法
定義 Settings
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
# 必填(沒有預設值)
database_url: str
secret_key: str
# 選填(有預設值)
debug: bool = False
app_name: str = "My App"
port: int = 8000
settings = Settings()設定環境變數
# .env 檔案
DATABASE_URL=postgresql://user:pass@localhost/db
SECRET_KEY=your-secret-key
DEBUG=true
APP_NAME=My Awesome App
PORT=3000或在 shell 設定:
export DATABASE_URL=postgresql://user:pass@localhost/db
export SECRET_KEY=your-secret-key環境變數命名
預設會把欄位名轉成大寫:
| Python 欄位 | 環境變數 |
|---|---|
database_url | DATABASE_URL |
secret_key | SECRET_KEY |
debug | DEBUG |
📁 讀取 .env 檔案
基本設定
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
database_url: str
secret_key: str
debug: bool = False
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8"
)多環境設定
from pydantic_settings import BaseSettings, SettingsConfigDict
import os
class Settings(BaseSettings):
database_url: str
secret_key: str
debug: bool = False
environment: str = "development"
model_config = SettingsConfigDict(
# 根據環境載入不同的 .env
env_file=(
".env",
f".env.{os.getenv('ENVIRONMENT', 'development')}"
),
env_file_encoding="utf-8",
extra="ignore" # 忽略 .env 中多餘的變數
)
# .env.development
# DEBUG=true
# DATABASE_URL=postgresql://localhost/dev_db
# .env.production
# DEBUG=false
# DATABASE_URL=postgresql://prod-server/prod_db🏷️ 環境變數前綴
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
database_url: str
redis_url: str
debug: bool = False
model_config = SettingsConfigDict(
env_prefix="MYAPP_" # 所有變數都要加前綴
)
# 環境變數:
# MYAPP_DATABASE_URL=...
# MYAPP_REDIS_URL=...
# MYAPP_DEBUG=true🔄 型別轉換
自動轉換
from pydantic_settings import BaseSettings
from typing import List, Set
class Settings(BaseSettings):
# 布林值
debug: bool # "true", "1", "yes" → True
# 數字
port: int # "8000" → 8000
timeout: float # "30.5" → 30.5
# 列表(JSON 或逗號分隔)
allowed_hosts: List[str] # '["host1","host2"]' 或 "host1,host2"
cors_origins: Set[str]
# 環境變數
# DEBUG=true
# PORT=8000
# ALLOWED_HOSTS=["localhost","127.0.0.1"]
# 或
# ALLOWED_HOSTS=localhost,127.0.0.1複雜型別
from pydantic_settings import BaseSettings
from pydantic import BaseModel
from typing import Optional
class DatabaseConfig(BaseModel):
host: str
port: int
name: str
user: str
password: str
class Settings(BaseSettings):
# 巢狀設定可以用 JSON
database: DatabaseConfig
# 環境變數(JSON 格式)
# DATABASE='{"host":"localhost","port":5432,"name":"mydb","user":"admin","password":"secret"}'使用前綴處理巢狀
from pydantic_settings import BaseSettings, SettingsConfigDict
class DatabaseSettings(BaseSettings):
host: str = "localhost"
port: int = 5432
name: str = "mydb"
user: str = "postgres"
password: str = ""
model_config = SettingsConfigDict(env_prefix="DB_")
class RedisSettings(BaseSettings):
host: str = "localhost"
port: int = 6379
db: int = 0
model_config = SettingsConfigDict(env_prefix="REDIS_")
class Settings(BaseSettings):
debug: bool = False
@property
def database(self) -> DatabaseSettings:
return DatabaseSettings()
@property
def redis(self) -> RedisSettings:
return RedisSettings()
# 環境變數
# DEBUG=true
# DB_HOST=localhost
# DB_PORT=5432
# DB_NAME=mydb
# DB_USER=admin
# DB_PASSWORD=secret
# REDIS_HOST=localhost
# REDIS_PORT=6379🔒 敏感資料處理
使用 SecretStr
from pydantic_settings import BaseSettings
from pydantic import SecretStr
class Settings(BaseSettings):
database_url: str
secret_key: SecretStr # 不會在日誌中顯示
api_key: SecretStr
settings = Settings()
# 存取
print(settings.secret_key) # SecretStr('**********')
print(settings.secret_key.get_secret_value()) # 實際的值
# 在日誌中安全
import logging
logging.info(f"Settings: {settings}") # secret_key 會被遮罩🚀 最佳實踐:完整設定範例
settings.py
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import SecretStr, field_validator, PostgresDsn
from typing import List, Optional
from functools import lru_cache
class Settings(BaseSettings):
"""應用程式設定"""
# ===== 基本設定 =====
app_name: str = "FastAPI App"
version: str = "1.0.0"
debug: bool = False
environment: str = "development"
# ===== 伺服器設定 =====
host: str = "0.0.0.0"
port: int = 8000
workers: int = 1
# ===== 資料庫設定 =====
database_url: str
database_pool_size: int = 5
database_max_overflow: int = 10
# ===== Redis 設定 =====
redis_url: str = "redis://localhost:6379/0"
# ===== 安全設定 =====
secret_key: SecretStr
algorithm: str = "HS256"
access_token_expire_minutes: int = 30
refresh_token_expire_days: int = 7
# ===== CORS 設定 =====
cors_origins: List[str] = ["http://localhost:3000"]
cors_allow_credentials: bool = True
# ===== 外部服務 =====
smtp_host: Optional[str] = None
smtp_port: int = 587
smtp_user: Optional[str] = None
smtp_password: Optional[SecretStr] = None
aws_access_key_id: Optional[str] = None
aws_secret_access_key: Optional[SecretStr] = None
aws_region: str = "ap-northeast-1"
s3_bucket: Optional[str] = None
# ===== 驗證 =====
@field_validator('environment')
@classmethod
def validate_environment(cls, v: str) -> str:
allowed = ['development', 'staging', 'production']
if v not in allowed:
raise ValueError(f'environment must be one of {allowed}')
return v
@field_validator('cors_origins', mode='before')
@classmethod
def parse_cors_origins(cls, v):
if isinstance(v, str):
return [origin.strip() for origin in v.split(',')]
return v
# ===== 計算屬性 =====
@property
def is_production(self) -> bool:
return self.environment == 'production'
@property
def is_development(self) -> bool:
return self.environment == 'development'
# ===== 設定 =====
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=False,
extra="ignore"
)
@lru_cache()
def get_settings() -> Settings:
"""獲取設定(使用快取)"""
return Settings()
# 方便直接 import
settings = get_settings().env.example
# 基本設定
APP_NAME=My FastAPI App
DEBUG=false
ENVIRONMENT=development
# 伺服器
HOST=0.0.0.0
PORT=8000
WORKERS=4
# 資料庫
DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/mydb
DATABASE_POOL_SIZE=10
DATABASE_MAX_OVERFLOW=20
# Redis
REDIS_URL=redis://localhost:6379/0
# 安全
SECRET_KEY=your-super-secret-key-change-in-production
ACCESS_TOKEN_EXPIRE_MINUTES=30
REFRESH_TOKEN_EXPIRE_DAYS=7
# CORS
CORS_ORIGINS=http://localhost:3000,http://localhost:8080
# SMTP(可選)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-password
# AWS(可選)
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_REGION=ap-northeast-1
S3_BUCKET=my-bucket使用設定
# main.py
from fastapi import FastAPI
from app.config import settings
app = FastAPI(
title=settings.app_name,
version=settings.version,
debug=settings.debug
)
# 在任何地方使用
from app.config import settings
print(settings.database_url)
print(settings.secret_key.get_secret_value())
print(settings.is_production)⚙️ 依賴注入中使用
from fastapi import FastAPI, Depends
from functools import lru_cache
from app.config import Settings
app = FastAPI()
@lru_cache()
def get_settings():
return Settings()
@app.get("/info")
async def info(settings: Settings = Depends(get_settings)):
return {
"app_name": settings.app_name,
"debug": settings.debug,
"environment": settings.environment
}✅ 重點總結
基本設定
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
database_url: str
debug: bool = False
model_config = SettingsConfigDict(
env_file=".env",
env_prefix="MYAPP_", # 可選
)環境變數對應
| 設定 | 環境變數 |
|---|---|
| 欄位名自動轉大寫 | debug → DEBUG |
| 使用前綴 | debug + prefix → MYAPP_DEBUG |
| case_sensitive=False | 不區分大小寫 |
型別轉換
| Python 型別 | 環境變數範例 |
|---|---|
bool | true, 1, yes |
int | 8000 |
List[str] | a,b,c 或 ["a","b"] |
SecretStr | secret (會被遮罩) |
🎤 面試這樣答
Q: 為什麼要用 pydantic-settings 而不是直接用 os.getenv?
答案:
- 型別安全:自動轉換和驗證型別(字串 “true” → bool True)
- 必填驗證:沒有預設值的欄位必須設定,啟動時就會報錯
- IDE 支援:有自動補全和型別提示
- 敏感資料:SecretStr 防止密碼洩漏到日誌
- 結構化:所有設定集中管理,不會散落各處
上一篇: 02-5. 序列化與反序列化 下一篇: 02-7. Pydantic v1 vs v2 遷移指南
最後更新:2025-12-17