2.1 推荐目录结构:app/core, app/api, app/schemas, app/main.py
在 FastAPI 项目中,良好的目录结构就像房子的地基——地基打得牢,房子才不会塌。我们采用模块化设计,把不同职责的代码分门别类放好,这样以后维护起来才不会“找不着北”。
一个典型的 FastAPI 项目结构如下:
my_fastapi_app/
├── app/
│ ├── __init__.py
│ ├── main.py # 应用入口,创建 FastAPI 实例
│ ├── core/ # 核心配置、安全、数据库连接等
│ │ └── __init__.py
│ ├── api/ # API 路由和端点
│ │ ├── __init__.py
│ │ └── v1/ # 版本化 API(可选)
│ ├── schemas/ # Pydantic 模型(请求/响应结构)
│ │ └── __init__.py
│ └── models/ # SQLAlchemy 模型(数据库表结构)
│ └── __init__.py
├── .env # 环境变量文件
├── requirements.txt # 依赖列表
└── manage.py # 启动脚本(可选)这种结构的好处是:高内聚、低耦合。比如你改数据库模型,只动 models/;加新接口,只写 api/;调返回格式,只改 schemas/。各司其职,互不干扰。
小结:推荐的目录结构将项目按功能拆分为核心、API、模型和数据校验层,便于团队协作和长期维护。
2.2 配置管理:使用 Pydantic Settings 加载 .env 文件
硬编码配置?那可是“祖传代码”的标志!我们用 Pydantic 的 BaseSettings 来优雅地管理配置,自动从 .env 文件加载环境变量。
首先,安装依赖(虽然 FastAPI 已包含 Pydantic,但确保版本兼容):
pip install pydantic[dotenv]然后创建 .env 文件:
# .env
APP_NAME=MyFastAPIApp
DEBUG=True
DATABASE_URL=mysql+asyncmy://user:password@localhost/dbname
SECRET_KEY=my_secret_key_123!接着在 app/core/config.py 中定义配置类:
# app/core/config.py
from pydantic_settings import BaseSettings # 注意:Pydantic v2 使用 pydantic_settings
class Settings(BaseSettings):
"""
应用配置类,自动从 .env 文件加载环境变量
"""
app_name: str = "FastAPI App"
debug: bool = False
database_url: str
secret_key: str
class Config:
env_file = ".env" # 指定 .env 文件路径
env_file_encoding = "utf-8"
# 创建全局配置实例
settings = Settings()注意:Pydantic v2 中,
BaseSettings已移到pydantic_settings模块,需单独安装pydantic-settings包。
现在,任何地方都可以导入 settings 使用配置:
from app.core.config import settings
print(settings.database_url) # 安全地获取数据库连接字符串| 功能名称 | 调用方法 | 说明 |
|---|---|---|
| 加载 .env | BaseSettings 自动加载 | 需设置 env_file,支持类型自动转换(如字符串转布尔值) |
| 默认值 | 字段赋默认值 | 如 debug: bool = False,若 .env 未设置则用默认值 |
| 必填项 | 不设默认值 | 如 database_url: str,若 .env 缺失会抛出 ValidationError |
小结:通过 Pydantic Settings,我们实现了配置的集中管理、类型安全和环境隔离,避免敏感信息硬编码。
2.3 启动脚本封装:统一入口 manage.py 或直接 uvicorn app.main:app
启动命令太长记不住?写个启动脚本能让你在同事面前显得很专业!
方式一:直接使用 Uvicorn(推荐开发时)
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000app.main:app:指向app/main.py中的app实例--reload:代码变更自动重启(仅开发环境!)
方式二:封装 manage.py(适合复杂场景)
# manage.py
import os
import sys
import uvicorn
from app.core.config import settings
def main():
"""统一启动入口"""
# 根据 DEBUG 设置是否启用 reload
reload = settings.debug
host = "0.0.0.0"
port = int(os.getenv("PORT", 8000))
uvicorn.run(
"app.main:app",
host=host,
port=port,
reload=reload,
log_level="info"
)
if __name__ == "__main__":
main()然后只需运行:
python manage.py这样做的好处是:启动参数集中管理,还能根据环境变量动态调整端口(方便 Docker 部署)。
小结:封装启动脚本简化了命令行操作,并支持根据配置动态调整运行参数,提升开发体验。
2.4 日志初始化:logging 配置基础格式
没有日志的系统就像黑夜开车——你不知道撞了什么。Python 内置的 logging 模块足够强大,我们来配置一个清晰的日志格式。
在 app/core/logging.py 中:
# app/core/logging.py
import logging
import sys
from app.core.config import settings
def setup_logging():
"""
初始化日志配置
"""
# 创建根日志记录器
logger = logging.getLogger()
logger.setLevel(logging.INFO if not settings.debug else logging.DEBUG)
# 避免重复添加处理器
if logger.handlers:
logger.handlers.clear()
# 创建控制台处理器
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
# 定义日志格式
formatter = logging.Formatter(
fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
handler.setFormatter(formatter)
# 添加处理器到日志记录器
logger.addHandler(handler)
# 禁止第三方库日志传播(可选)
logging.getLogger("uvicorn").setLevel(logging.WARNING)
logging.getLogger("sqlalchemy").setLevel(logging.WARNING)然后在 app/main.py 中初始化:
# app/main.py
from fastapi import FastAPI
from app.core.logging import setup_logging
from app.core.config import settings
# 初始化日志
setup_logging()
app = FastAPI(
title=settings.app_name,
debug=settings.debug
)
@app.get("/")
def read_root():
import logging
logging.info("Root endpoint accessed")
return {"Hello": "World"}现在,你的日志会像这样:
2023-10-05 14:30:00 - root - INFO - Root endpoint accessed| 功能名称 | 调用方法 | 说明 |
|---|---|---|
| 设置日志级别 | logger.setLevel() | 开发用 DEBUG,生产用 INFO/WARNING |
| 自定义格式 | logging.Formatter | 包含时间、模块名、日志级别和消息 |
| 控制第三方日志 | logging.getLogger("lib").setLevel() | 避免 SQLAlchemy 或 Uvicorn 刷屏 |
小结:合理的日志配置能帮助快速定位问题,清晰的格式让日志可读性大大提升,是生产环境的必备品。