plugins/languages/python/skills/web/SKILL.md
Python Web 后端开发规范 (FastAPI / Litestar + Pydantic v2 + SQLAlchemy 2.0 async)。涵盖路由组织、依赖注入、请求响应模型、中间件、lifespan、认证、错误处理。在写 REST API、设计 ORM 模型、加中间件、做 OpenAPI 文档、迁移 Flask/Django 时使用。也触发于"FastAPI"、"Litestar"、"REST API"、"Pydantic v2"、"SQLAlchemy 异步"。
npx skillsauth add lazygophers/ccplugin python-webInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
默认 FastAPI 0.115+, 替代选择 Litestar (更现代, 性能更好)。两者 Pydantic v2 + ASGI 都通用, 下文以 FastAPI 为例。
| 框架 | 何时用 | |------|--------| | FastAPI | 默认, 生态最大, 招聘最容易, OpenAPI 一流 | | Litestar | 新项目追求性能 + DI 更强 (channels, msgspec 集成) | | Starlette | 自己拼框架 / 极简 ASGI 中间件 | | Django + DRF | 重度后台 (admin, ORM, auth 套件), 老团队迁移成本 | | Flask | 不推荐用于新项目 (无 async 一等公民, 生态停滞) |
按领域分层, 不按文件类型分:
src/myapp/
├── main.py # FastAPI() 实例 + lifespan
├── api/
│ ├── deps.py # 依赖 (get_db, get_current_user)
│ ├── routers/
│ │ ├── users.py
│ │ └── orders.py
│ └── errors.py # exception_handler
├── domain/
│ ├── users/
│ │ ├── models.py # SQLAlchemy ORM
│ │ ├── schemas.py # Pydantic
│ │ ├── service.py # 业务逻辑
│ │ └── repo.py # 数据访问
│ └── orders/...
├── core/
│ ├── config.py # pydantic-settings
│ ├── db.py # engine, session factory
│ └── logging.py
└── tests/
按文件类型分 (models/, schemas/, services/) 在 ≥10 个领域时混乱。
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动
app.state.db = await create_db_engine(settings.db_url)
app.state.http = httpx.AsyncClient()
yield
# 关闭
await app.state.http.aclose()
await app.state.db.dispose()
app = FastAPI(title="MyApp", lifespan=lifespan)
app.include_router(users_router, prefix="/api/v1/users", tags=["users"])
不要用 @app.on_event("startup") (已 deprecated)。
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_prefix="APP_")
db_url: str
secret_key: str
debug: bool = False
settings = Settings() # 从环境变量 + .env 加载
不要 os.getenv("DB_URL") 散落到处。
输入输出模型分离, 永远不要把 ORM 对象直接返回:
# schemas.py
from pydantic import BaseModel, ConfigDict, EmailStr
class UserCreate(BaseModel):
username: str
email: EmailStr
password: str # 输入有 password
class UserRead(BaseModel):
model_config = ConfigDict(from_attributes=True) # 从 ORM 转
id: int
username: str
email: EmailStr
# 没有 password
response_model=UserRead 确保 API 不泄漏敏感字段:
@router.post("", response_model=UserRead, status_code=201)
async def create_user(payload: UserCreate, db: DbSession) -> User:
return await user_service.create(db, payload)
Annotated[T, Depends(...)] 替代 Depends() 默认值 (类型检查友好):
from typing import Annotated
from fastapi import Depends
async def get_db(request: Request) -> AsyncIterator[AsyncSession]:
async with AsyncSession(request.app.state.db) as session:
yield session
DbSession = Annotated[AsyncSession, Depends(get_db)]
async def get_current_user(
token: Annotated[str, Depends(oauth2_scheme)],
db: DbSession,
) -> User:
return await auth_service.verify(db, token)
CurrentUser = Annotated[User, Depends(get_current_user)]
@router.get("/me", response_model=UserRead)
async def read_me(user: CurrentUser) -> User:
return user
类型别名 (DbSession, CurrentUser) 让签名清爽。
每个领域一个 router, 路由函数只编排, 业务在 service 层:
# api/routers/users.py
router = APIRouter()
@router.get("/{user_id}", response_model=UserRead)
async def read_user(user_id: int, db: DbSession) -> User:
user = await user_service.get(db, user_id)
if user is None:
raise NotFoundError("user", user_id)
return user
路由函数不超过 20 行, 不写 SQL, 不直接调 httpx。
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy import select
class Base(DeclarativeBase): ...
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
username: Mapped[str] = mapped_column(unique=True)
email: Mapped[str]
# 查询用 2.0 风格
async def get_by_email(db: AsyncSession, email: str) -> User | None:
stmt = select(User).where(User.email == email)
return (await db.execute(stmt)).scalar_one_or_none()
不要再用 db.query(User) (1.x legacy API)。
业务层 raise 领域异常 (见 python-error), 在 app 层翻译成 HTTP:
@app.exception_handler(NotFoundError)
async def not_found(req: Request, exc: NotFoundError):
return JSONResponse(404, {"error": "not_found", "resource": exc.resource})
@app.exception_handler(ValidationError)
async def bad_request(req: Request, exc: ValidationError):
return JSONResponse(400, {"error": "validation", "field": exc.field})
不要在 service 里 raise HTTPException (耦合 Web 层)。
from starlette.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins,
allow_methods=["*"],
allow_headers=["*"],
)
@app.middleware("http")
async def request_id_middleware(request: Request, call_next):
rid = request.headers.get("x-request-id", str(uuid.uuid4()))
structlog.contextvars.bind_contextvars(request_id=rid)
response = await call_next(request)
response.headers["x-request-id"] = rid
return response
JWT + OAuth2 用 python-jose 或 authlib, 密码用 argon2-cffi (不要 bcrypt, 不要 sha256):
from argon2 import PasswordHasher
ph = PasswordHasher()
hashed = ph.hash(password)
ph.verify(hashed, password_attempt) # 抛异常 = 失败
见 python-testing 的"异步测试"章节。FastAPI 用 httpx.AsyncClient + ASGITransport, 不用 TestClient。
/docs 自动生成。提升质量:
summary, descriptiontagsresponses={404: {"model": ErrorResponse}}Field(..., description="...", examples=["..."])response_model= + Pydantic schema)Depends() 当默认值参数 (用 Annotated[T, Depends(...)])@app.on_event("startup") (用 lifespan)requests / 同步 ORM 在 async 路由里 (阻塞 event loop)UserReadTestClient 测异步 app (用 httpx.AsyncClient)tools
UI/UX 与布局设计——做界面布局/结构/导航/组件/交互的设计决策。触发:做UI/UX/布局/排版/导航/组件/交互/栅格/响应式/图表选型/字体配对。按媒介路由 HTML/Web、原生 App(iOS/Android/桌面)、CLI、TUI。需后端动态系统不适用;配色/主题/色板走姊妹 skill design-color。
tools
主题与配色设计——做颜色搭配/调色板/主题/品牌色阶/暗模式的设计决策。触发:选配色/调色/主题/色板/品牌色/暗模式/对比度/色盲/UI风格。按媒介路由 HTML/Web(CSS变量)、原生App(平台token)、CLI(ANSI)、TUI(真彩/256/16降级)。保证可访问性(对比度/色盲安全)。需后端动态系统不适用;UI/UX 布局/组件/交互走姊妹 skill design-uiux。
tools
跨任意组件(plugin/skill/agent/command)的验证驱动优化循环纪律 skill。当用户要优化某个已有组件却无明确方向、或要防止改了反而更差(自评乐观偏差 / 多维同改归因失效 / 为凑分加废话膨胀)、或要把一套通用「评分→单变量改→改后验证严格更好才留否则回滚→触顶停」的纪律套到任意组件上时使用。管优化过程本身的纪律(validation gate / ratchet / 独立验证 / 触顶停),不评单组件深度(交 skill-dev),不查插件接线(交 plugin-dev)。仅手动 /optimize-any 触发。
data-ai
两层规则记忆 (基于 .skein/spec)。planning 时 recall 召回相关规则、task finish 后 sediment 沉淀学习 + prune 自动精简过期/重复/断链规则。core 常驻硬规 + recall 按需召回, 经判定门自动写盘 (不逐次问用户)。产出 .skein/spec 下 core/recall 规则文件 + index。另支持空仓 bootstrap 播种规则基线、记忆大面积失效 (大重构/换栈) 时 reconstruct 可逆归档后按项目类型分型重建、maintain 手动体检 (超预算/stale/断链/重复/废弃, --apply 自动修复)、auto-fix (Stop hook 写 .pending-fix 标记 → main 派 skein-specer bg 跑 maintain --apply 全自动修, 断链只报告)。