PythonFastAPI开发Skill python-fastapi

专家模式,用于构建使用 FastAPI、uv 包管理器、模块化架构和 SQLAlchemy 数据库集成的 Python API。

后端开发 0 次安装 2 次浏览 更新于 2/28/2026

Python FastAPI 开发 使用 uv 包管理器、模块化项目结构、SQLAlchemy ORM 和生产就绪模式构建 Python API 的专家模式。

技术栈

  • 运行时: Python 3.12+
  • 包管理器: uv(快速,基于 Rust)
  • 框架: FastAPI
  • ORM: SQLAlchemy 2.0(异步)
  • 验证: Pydantic v2
  • 数据库: PostgreSQL(或 SQLite 用于开发)
  • 迁移: Alembic
  • 测试: pytest, pytest-asyncio
  • 代码检查: ruff

项目结构

按领域组织的特征模块化架构 - 代码按领域组织,而不是按层:

我的项目/
├── pyproject.toml           # 带有 uv 的项目配置
├── uv.lock                   # 锁定文件
├── .python-version           # Python 版本
├── .env                      # 环境变量
├── .env.example
├── alembic.ini               # Alembic 配置
├── alembic/                  # 迁移
│   ├── env.py
│   ├── script.py.mako
│   └── versions/
├── src/
│   └── app/
│       ├── __init__.py
│       ├── main.py           # FastAPI 应用入口
│       ├── config.py         # 设置
│       ├── database.py       # 数据库会话
│       ├── core/
│       │   ├── __init__.py
│       │   ├── dependencies.py  # 共享依赖项
│       │   ├── exceptions.py    # 自定义异常
│       │   ├── middleware.py    # 中间件
│       │   └── security.py      # 认证工具
│       ├── models/
│       │   ├── __init__.py
│       │   └── base.py          # SQLAlchemy 基础和混合
│       ├── features/
│       │   ├── __init__.py
│       │   ├── auth/
│       │   │   ├── __init__.py
│       │   │   ├── api.py       # 认证端点
│       │   │   ├── schemas.py   # 认证 Pydantic 模式
│       │   │   ├── services.py  # 认证业务逻辑
│       │   │   ├── models.py    # 认证 SQLAlchemy 模型
│       │   │   └── utils.py     # 认证助手(JWT 等)
│       │   ├── users/
│       │   │   ├── __init__.py
│       │   │   ├── api.py       # 用户端点
│       │   │   ├── schemas.py   # 用户 Pydantic 模式
│       │   │   ├── services.py  # 用户业务逻辑
│       │   │   ├── models.py    # 用户 SQLAlchemy 模型
│       │   │   └── repository.py # 用户数据访问
│       │   └── items/
│       │       ├── __init__.py
│       │       ├── api.py
│       │       ├── schemas.py
│       │       ├── services.py
│       │       └── models.py
│       └── api/
│           ├── __init__.py
│           └── router.py        # 聚合所有功能路由器
└── tests/
    ├── __init__.py
    ├── conftest.py
    ├── features/
    │   ├── auth/
    │   │   └── test_auth.py
    │   └── users/
    │       └── test_users.py
    └── integration/

使用 uv 快速设置

# 安装 uv
curl -LsSf https://astral.sh/uv/install.sh | sh

# 创建新项目
uv init my-project
cd my-project

# 设置 Python 版本
uv python pin 3.12

# 添加依赖项
uv add fastapi uvicorn[standard] sqlalchemy[asyncio] asyncpg
uv add pydantic pydantic-settings python-dotenv
uv add alembic

# 添加开发依赖项
uv add --dev pytest pytest-asyncio pytest-cov httpx ruff mypy

# 创建源结构
mkdir -p src/app/{api/v1/endpoints,core,models,schemas,services,repositories}
touch src/app/__init__.py

核心模式

pyproject.toml

[项目]
名称 = "my-project"
版本 = "0.1.0"
描述 = "FastAPI 应用程序"
需要 Python = ">=3.12"
依赖 = [
    "fastapi>=0.115.0",
    "uvicorn[standard]>=0.32.0",
    "sqlalchemy[asyncio]>=2.0.0",
    "asyncpg>=0.30.0",
    "pydantic>=2.10.0",
    "pydantic-settings>=2.6.0",
    "python-dotenv>=1.0.0",
    "alembic>=1.14.0",
]

[项目.可选依赖]
开发 = [
    "pytest>=8.0.0",
    "pytest-asyncio>=0.24.0",
    "pytest-cov>=6.0.0",
    "httpx>=0.28.0",
    "ruff>=0.8.0",
    "mypy>=1.13.0",
]

[工具.ruff]
目标版本 = "py312"
行长度 = 88

[工具.ruff.lint]
选择 = ["E", "F", "I", "N", "W", "UP", "B", "C4", "SIM"]

[工具.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]

[工具.mypy]
python_version = "3.12"
严格 = true

配置(src/app/config.py)

从 functools import lru_cache
从 pydantic_settings import BaseSettings, SettingsConfigDict


类 Settings(BaseSettings):
    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        case_sensitive=False,
    )

    # App
    app_name: str = "My API"
    debug: bool = False
    api_v1_prefix: str = "/api/v1"

    # 数据库
    database_url: str = "postgresql+asyncpg://user:pass@localhost:5432/db"

    # 安全
    secret_key: str = "change-me-in-production"
    access_token_expire_minutes: int = 30


@lru_cache
def get_settings() -> Settings:
    return Settings()


设置 = get_settings()

数据库设置(src/app/database.py)

从 collections.abc import AsyncGenerator
从 sqlalchemy.ext.asyncio import (
    AsyncSession,
    async_sessionmaker,
    create_async_engine,
)

从 app.config import 设置

引擎 = create_async_engine(
    设置.database_url,
    echo=设置.debug,
    pool_pre_ping=True,
)

async_session_maker = async_sessionmaker(
    引擎,
    class_=AsyncSession,
    expire_on_commit=False,
)


异步 def get_db() -> AsyncGenerator[AsyncSession, None]:
    异步与 async_session_maker() 作为 session:
        尝试:
            产量 session
            等待 session.commit()
        除了 Exception:
            等待 session.rollback()
            引发

SQLAlchemy 基础模型(src/app/models/base.py)

从 datetime import datetime
从 sqlalchemy import DateTime, func
从 sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


类 Base(DeclarativeBase):
    通过


类 TimestampMixin:
    created_at: Mapped[datetime] = mapped_column(
         DateTime(timezone=True),
         服务器默认=func.now(),
     )
    updated_at: Mapped[datetime] = mapped_column(
         DateTime(timezone=True),
         服务器默认=func.now(),
         onupdate=func.now(),
     )

功能模块模式

每个功能都是自包含的,有自己的 api、模式、服务、模型和工具。

功能:users/schemas.py

从 pydantic import BaseModel, EmailStr, ConfigDict


类 UserBase(BaseModel):
    email: EmailStr
    full_name: str | None = None


类 UserCreate(UserBase):
    password: str


类 UserUpdate(BaseModel):
    email: EmailStr | None = None
    full_name: str | None = None
    password: str | None = None


类 UserResponse(UserBase):
    model_config = ConfigDict(from_attributes=True)
    id: int
    is_active: bool

功能:users/models.py

从 sqlalchemy import String
从 sqlalchemy.orm import Mapped, mapped_column

从 app.models.base import Base, TimestampMixin


类 User(Base, TimestampMixin):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)
    email: Mapped[str] = mapped_column(String(255), unique=True, index=True)
    hashed_password: Mapped[str] = mapped_column(String(255))
    full_name: Mapped[str | None] = mapped_column(String(255))
    is_active: Mapped[bool] = mapped_column默认=True)

功能:users/repository.py

从 sqlalchemy import select
从 sqlalchemy.ext.asyncio import AsyncSession

从 app.features.users.models import User


类 UserRepository:
    防御 __init__(self, db: AsyncSession):
        自我.db = db

    异步 def get(self, id: int) -> User | None:
        结果 = 等待 self.db.execute(select(User).where(User.id == id))
        返回结果.scalar_one_or_none()

    异步 def get_by_email(self, email: str) -> User | None:
        结果 = 等待 self.db.execute(select(User).where(User.email == email))
        返回结果.scalar_one_or_none()

    异步 def get_all(self, skip: int = 0, limit: int = 100) -> 列表[User]:
        结果 = 等待 self.db.execute(select(User).offset(skip).limit(limit))
        返回列表结果.scalars().all()

    异步 def create(self, data: 字典) -> User:
        用户 = User(**数据)
        自我.db.add(user)
        等待 self.db.flush()
        等待 self.db.refresh(user)
        返回用户

    异步 def update(self, 用户: User, 数据: 字典) -> User:
        对于字段,值 在 数据.items():
            如果值不是 None:
                设置attr(user, field, value)
        等待 self.db.flush()
        等待 self.db.refresh(user)
        返回用户

功能:users/services.py

从 sqlalchemy.ext.asyncio import AsyncSession

从 app.core.security import hash_password
从 app.features.users.models import User
从 app.features.users.repository import UserRepository
从 app.features.users.schemas import UserCreate, UserUpdate


类 UserService:
    防御 __init__(self, db: AsyncSession):
        自我.db = db
        自我.repo = UserRepository(db)

    异步 def get(self, user_id: int) -> User | None:
        返回等待 self.repo.get(user_id)

    异步 def get_by_email(self, email: str) -> User | None:
        返回等待 self.repo.get_by_email(email)

    异步 def list(self, skip: int = 0, limit: int = 100) -> 列表[User]:
        返回等待 self.repo.get_all(skip=skip, limit=limit)

    异步 def create(self, user_in: UserCreate) -> User:
        数据 = user_in.model_dump()
        数据["hashed_password"] = hash_password(data.pop("password"))
        返回等待 self.repo.create(data)

    异步 def update(self, 用户: User, user_in: UserUpdate) -> User:
        数据 = user_in.model_dump(exclude_unset=True)
        如果 "password" 在数据:
            数据["hashed_password"] = hash_password(data.pop("password"))
        返回等待 self.repo.update(user, data)

功能:users/api.py

从 fastapi import APIRouter, Depends, HTTPException, 状态
从 sqlalchemy.ext.asyncio import AsyncSession

从 app.database import get_db
从 app.features.users.schemas import UserCreate, UserResponse, UserUpdate
从 app.features.users.services import UserService

路由器 = APIRouter(prefix="/users", tags=["users"])


异步 def get_service(db: AsyncSession = Depends(get_db)) -> UserService:
    返回 UserService(db)


@router.get("", response_model=list[UserResponse])
异步 def list_users(
    skip: int = 0,
    limit: int = 100,
    服务: UserService = Depends(get_service),
):
    返回等待 服务.list(skip=skip, limit=limit)


@router.get("/{user_id}", response_model=UserResponse)
异步 def get_user(
    user_id: int,
    服务: UserService = Depends(get_service),
):
    用户 = 等待 服务.get(user_id)
    如果不 用户:
        引发 HTTPException状态_code=404, detail="User not found"
    返回 用户


@router.post("", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
异步 def create_user(
    user_in: UserCreate,
    服务: UserService = Depends(get_service),
):
    如果 等待 服务.get_by_email(user_in.email):
        引发 HTTPException状态_code=400, detail="Email already registered"
    返回等待 服务.create(user_in)


@router.patch("/{user_id}", response_model=UserResponse)
异步 def update_user(
    user_id: int,
    user_in: UserUpdate,
    服务: UserService = Depends(get_service),
):
    用户 = 等待 服务.get(user_id)
    如果不 用户:
        引发 HTTPException状态_code=404, detail="User not found"
    返回等待 服务.update(user, user_in)

功能:users/init.py(导出)

从 app.features.users.api import router
从 app.features.users.models import User
从 app.features.users.schemas import UserCreate, UserResponse, UserUpdate
从 app.features.users.services import UserService

__all__ = ["router", "User", "UserCreate", "UserResponse", "UserUpdate", "UserService"]

主路由器(src/app/api/router.py)

从 fastapi import APIRouter

从 app.features.auth import router 作为 auth_router
从 app.features.users import router 作为 users_router
从 app.features.items import router 作为 items_router

api_router = APIRouter()
api_router.include_router(auth_router)
api_router.include_router(users_router)
api_router.include_router(items_router)

FastAPI 应用(src/app/main.py)

从 contextlib import asynccontextmanager
从 collections.abc import AsyncIterator

从 fastapi import FastAPI
从 fastapi.middleware.cors import CORSMiddleware

从 app.api.router import api_router
从 app.config import 设置
从 app.database import 引擎


@asynccontextmanager
异步 def lifespan(app: FastAPI) -> AsyncIterator[None]:
    # 启动
    产量
    # 关闭
    等待 引擎.dispose()

应用 = FastAPI(
    标题=设置.app_name,
    openapi_url=f"{设置.api_v1_prefix}/openapi.json",
    lifespan=lifespan,
)

应用.add_middleware(
    CORSMiddleware,
    允许_origins=["*"],  # 为生产配置
    允许_credentials=True,
    允许_methods=["*"],
    允许_headers=["*"],
)

应用.include_router(api_router, prefix=设置.api_v1_prefix)


@应用.get("/health")
异步 def health_check():
    返回 {"status": "healthy"}

数据库迁移与 Alembic

# 初始化 Alembic
uv run alembic init alembic

# 更新 alembic/env.py 为异步
# 然后创建迁移
uv run alembic revision --autogenerate -m "Initial migration"

# 应用迁移
uv run alembic upgrade head

异步 Alembic env.py

导入 asyncio
从 logging.config import fileConfig

从 sqlalchemy import pool
从 sqlalchemy.ext.asyncio import async_engine_from_config
从 alembic import context

从 app.config import 设置
从 app.models.base import Base
从 app.models import 用户, 项目  # 导入所有模型

配置 = context.config
配置.set_main_option("sqlalchemy.url", 设置.database_url)

如果 config.config_file_name 是 None:
    fileConfig(config.config_file_name)

target_metadata = Base.metadata

防御 run_migrations_offline() -> None:
    url = config.get_main_option("sqlalchemy.url")
    context.configure(
        url=url,
        目标_metadata=target_metadata,
        字面绑定=True,
        方言_opts={"paramstyle": "named"},
    )
    与 context.begin_transaction():
        context.run_migrations()

防御 do_run_migrations(connection) -> None:
    context.configure(connection=connection, 目标_metadata=target_metadata)
    与 context.begin_transaction():
        context.run_migrations()


异步 def run_async_migrations() -> None:
    connectable = async_engine_from_config(
        config.get_section(config.config_ini_section, {}),
        前缀="sqlalchemy.",
        poolclass=pool.NullPool,
    )
    异步与 connectable.connect() 作为连接:
        等待连接.run_sync(do_run_migrations)
    等待 connectable.dispose()

防御 run_migrations_online() -> None:
    asyncio.run(run_async_migrations())


如果 context.is_offline_mode():
    run_migrations_offline()
否则:
    run_migrations_online()

测试

conftest.py

导入 pytest
从 httpx import ASGITransport, AsyncClient
从 sqlalchemy.ext.asyncio import 创建_async_engine, async_sessionmaker, AsyncSession

从 app.main import 应用
从 app.database import get_db
从 app.models.base import Base


@pytest.fixture
异步 def db_session():
    引擎 = 创建_async_engine(
        "sqlite+aiosqlite:///:memory:",
        echo=True,
    )
    异步与引擎.begin() 作为 conn:
        等待 conn.run_sync(Base.metadata.create_all)

    会话制造商 = async_sessionmaker(引擎, expire_on_commit=False)
    异步与会话制造商() 作为会话:
        产量会话

    等待引擎.dispose()


@pytest.fixture
异步 def client(db_session: AsyncSession):
    异步 def override_get_db():
        产量 db_session

    应用.dependency_overrides[get_db] = override_get_db
    异步与 AsyncClient(
        transport=ASGITransport(应用=应用),
        base_url="http://test"
    ) 作为 ac:
        产量 ac
    应用.dependency_overrides.clear()

示例测试

导入 pytest
从 httpx import AsyncClient


@pytest.mark.asyncio
异步 def test_create_user(client: AsyncClient):
    响应 = 等待 client.post(
        "/api/v1/users",
        json={
            "email": "test@example.com",
            "password": "testpassword123",
            "full_name": "Test User",
        },
    )
    断言响应.status_code == 201
    数据 = 响应.json()
    断言数据["email"] == "test@example.com"
    断言 "id" 在 数据

运行应用程序

# 开发
uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8000

# 生产
uv run uvicorn app.main:app --host 0.0.0.0 --port 8000 --works 4

# 运行测试
uv run pytest -v

# 运行覆盖
uv run pytest --cov=app --cov-report=html

# 代码检查和格式化
uv run ruff check .
uv run ruff format .

# 类型检查
uv run mypy src/

最佳实践

  1. 分层架构

    • 路由:处理 HTTP,验证,响应格式化
    • 服务:业务逻辑,编排
    • 仓库:数据访问,查询
  2. 依赖注入

    • 使用 FastAPI 的 Depends() 进行清晰的 DI
    • 注入数据库会话,服务,配置
  3. 类型安全

    • 使用 Pydantic 用于所有请求/响应模式
    • 使用 SQLAlchemy 2.0 映射列与类型
    • 启用严格的 mypy
  4. 异步优先

    • 整个过程中使用 async/await
    • 使用 asyncpg 用于 PostgreSQL
    • 使用 aiosqlite 用于测试
  5. 配置

    • 使用 pydantic-settings 用于类型安全的配置
    • 从环境变量中加载
    • 永远不要提交秘密
  6. 测试

    • 使用内存 SQLite 用于单元测试
    • 使用测试容器用于集成测试
    • 模拟外部服务