Skip to content

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,但确保版本兼容):

bash
pip install pydantic[dotenv]

然后创建 .env 文件:

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 中定义配置类:

python
# 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 使用配置:

python
from app.core.config import settings

print(settings.database_url)  # 安全地获取数据库连接字符串
功能名称调用方法说明
加载 .envBaseSettings 自动加载需设置 env_file,支持类型自动转换(如字符串转布尔值)
默认值字段赋默认值debug: bool = False,若 .env 未设置则用默认值
必填项不设默认值database_url: str,若 .env 缺失会抛出 ValidationError

小结:通过 Pydantic Settings,我们实现了配置的集中管理、类型安全和环境隔离,避免敏感信息硬编码。


2.3 启动脚本封装:统一入口 manage.py 或直接 uvicorn app.main:app

启动命令太长记不住?写个启动脚本能让你在同事面前显得很专业!

方式一:直接使用 Uvicorn(推荐开发时)

bash
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
  • app.main:app:指向 app/main.py 中的 app 实例
  • --reload:代码变更自动重启(仅开发环境!)

方式二:封装 manage.py(适合复杂场景)

python
# 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()

然后只需运行:

bash
python manage.py

这样做的好处是:启动参数集中管理,还能根据环境变量动态调整端口(方便 Docker 部署)。

小结:封装启动脚本简化了命令行操作,并支持根据配置动态调整运行参数,提升开发体验。


2.4 日志初始化:logging 配置基础格式

没有日志的系统就像黑夜开车——你不知道撞了什么。Python 内置的 logging 模块足够强大,我们来配置一个清晰的日志格式。

app/core/logging.py 中:

python
# 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 中初始化:

python
# 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 刷屏

小结:合理的日志配置能帮助快速定位问题,清晰的格式让日志可读性大大提升,是生产环境的必备品。