| name | fastapi-crud |
| description | Guides FastAPI CRUD API development following project conventions. Invoke when creating or modifying API endpoints. |
FastAPI CRUD API 开发指南
本技能提供完整的FastAPI增删改查接口开发指导,严格遵循项目统一风格。
项目架构概览
app/
├── api/
│ ├── v1/tenant/ # 租户模块API
│ │ └── xxx.py # 业务API路由
│ ├── dependencies.py # 依赖注入(认证、数据库)
│ ├── responses.py # 统一响应格式
│ └── service_dependencies.py # Service依赖注入
├── schemas/ # Pydantic视图模型
│ └── xxx.py
├── services/ # 业务逻辑层
│ └── xxx_service.py
└── repositories/ # 数据访问层
└── xxx_repository.py
依赖注入体系
1. 数据库会话注入
from app.api.dependencies import get_db_by_tenant, get_current_user
from app.core.current_user import CurrentUser
from sqlalchemy.ext.asyncio import AsyncSession
async def get_xxx(
current_user: CurrentUser = Depends(get_current_user),
db: AsyncSession = Depends(get_db_by_tenant)
):
pass
2. Service依赖注入
from app.api.service_dependencies import get_service_dependency
from app.services.xxx_service import XxxService
async def get_xxx(
service: XxxService = Depends(get_service_dependency(XxxService)),
current_user: CurrentUser = Depends(get_current_user)
):
pass
3. CurrentUser类型
from app.core.current_user import CurrentUser
class CurrentUser(BaseModel):
user_id: int = 0
user_num: str = ""
user_name: str = ""
user_role: str = ""
tenant_id: str = ""
userrole_id: int = 0
is_authenticated: bool = False
统一响应格式
响应函数
from app.api.responses import success_response, error_response
return success_response(data)
return success_response(data, msg="操作成功")
return success_response({"id": 123, "name": "xxx"})
return error_response("错误信息")
return error_response(msg="操作失败", code=400)
响应格式示例
{
"isSuccess": true,
"msg": "操作成功",
"code": 0,
"data": { ... }
}
{
"isSuccess": false,
"msg": "错误信息",
"code": 400,
"error": "详细错误信息"
}
Schema定义模式
文件位置
app/schemas/xxx.py
视图模型模板
from typing import Optional
from pydantic import BaseModel, Field
class XxxViewModel(BaseModel):
"""用于接口返回的视图模型"""
id: int = Field(default=0, description="主键ID")
field1: str = Field(default="", description="字段1")
field2: int = Field(default=0, description="字段2")
tenant_id: str = Field(default="", description="租户ID")
class XxxCreateViewModel(BaseModel):
"""用于创建操作的视图模型"""
field1: str = Field(..., description="字段1(必填)")
field2: int = Field(default=0, description="字段2(可选)")
field3: Optional[str] = Field(default="", description="字段3")
class XxxUpdateViewModel(BaseModel):
"""用于更新操作的视图模型"""
id: int = Field(..., description="主键ID(必填)")
field1: Optional[str] = Field(None, description="字段1")
field2: Optional[int] = Field(None, description="字段2")
设计原则
- ViewModel: 返回给前端的完整数据,字段都有默认值
- CreateViewModel: 创建时需要提交的字段,必填字段用
...,可选字段用default=
- UpdateViewModel: 更新时提交的字段,必填字段用
...,其他字段用Optional
Router开发模板
标准CRUD接口文件结构
from fastapi import APIRouter, Depends, Query
from typing import List, Optional
from app.schemas.xxx import (
XxxViewModel,
XxxCreateViewModel,
XxxUpdateViewModel
)
from app.services.xxx_service import XxxService
from app.api.service_dependencies import get_service_dependency
from app.api.responses import success_response, error_response
from app.api.dependencies import get_current_user
from app.core.current_user import CurrentUser
router = APIRouter()
@router.get("/xxx", response_model=XxxViewModel)
async def get_xxx(
id: int = Query(..., description="XXX ID"),
service: XxxService = Depends(get_service_dependency(XxxService)),
current_user: CurrentUser = Depends(get_current_user)
):
"""
根据ID获取XXX信息
Args:
id: XXX ID
service: XXX服务
current_user: 当前用户
Returns:
XXX信息
"""
result = await service.get_by_id(id, current_user)
if result is None:
return error_response("记录不存在")
return success_response(result)
@router.get("/xxx/list", response_model=List[XxxViewModel])
async def get_xxx_list(
service: XxxService = Depends(get_service_dependency(XxxService)),
current_user: CurrentUser = Depends(get_current_user)
):
"""
获取所有XXX列表
Args:
service: XXX服务
current_user: 当前用户
Returns:
XXX列表
"""
result = await service.get_all(current_user)
return success_response(result)
@router.get("/xxx/page")
async def get_xxx_page(
page_index: int = Query(1, ge=1, description="页码"),
page_size: int = Query(10, ge=1, le=100, description="每页数量"),
field1: Optional[str] = Query(None, description="字段1"),
field2: Optional[int] = Query(None, description="字段2"),
service: XxxService = Depends(get_service_dependency(XxxService)),
current_user: CurrentUser = Depends(get_current_user)
):
"""
分页查询XXX列表
Args:
page_index: 页码,从1开始
page_size: 每页数量
field1: 字段1(模糊查询)
field2: 字段2
service: XXX服务
current_user: 当前用户
Returns:
XXX列表和总数
"""
search_params = {}
if field1:
search_params["field1"] = field1
if field2:
search_params["field2"] = field2
data, totals = await service.page_search(
page_index, page_size, search_params, current_user
)
return success_response({
"data": data,
"totals": totals,
"page_index": page_index,
"page_size": page_size
})
@router.post("/xxx", response_model=XxxViewModel)
async def create_xxx(
view_model: XxxCreateViewModel,
service: XxxService = Depends(get_service_dependency(XxxService)),
current_user: CurrentUser = Depends(get_current_user)
):
"""
创建XXX
Args:
view_model: XXX创建数据
service: XXX服务
current_user: 当前用户
Returns:
创建的XXX信息
"""
id, msg = await service.add(view_model, current_user)
if id == 0:
return error_response(msg)
result = await service.get_by_id(id)
return success_response(result)
@router.post("/xxx/update", response_model=XxxViewModel)
async def update_xxx(
view_model: XxxUpdateViewModel,
service: XxxService = Depends(get_service_dependency(XxxService)),
current_user: CurrentUser = Depends(get_current_user)
):
"""
更新XXX
Args:
view_model: XXX更新数据
service: XXX服务
current_user: 当前用户
Returns:
更新后的XXX信息
"""
flag, msg = await service.update(view_model.id, view_model, current_user)
if not flag:
return error_response(msg)
result = await service.get_by_id(view_model.id, current_user)
return success_response(result)
@router.post("/xxx/delete")
async def delete_xxx(
id: int = Query(..., description="XXX ID"),
service: XxxService = Depends(get_service_dependency(XxxService)),
current_user: CurrentUser = Depends(get_current_user)
):
"""
删除XXX
Args:
id: XXX ID
service: XXX服务
current_user: 当前用户
Returns:
删除结果
"""
flag, msg = await service.delete(id, current_user)
if not flag:
return error_response(msg)
return success_response({"id": id})
接口命名规范
1. URL路径规范
- 小写字母 + 中划线分隔:
/xxx/yyy-zzz
- 避免驼峰和下划线
@router.get("/warehouse-location")
@router.get("/inbound-order")
@router.post("/stock-update")
@router.get("/warehouseLocation")
@router.get("/warehouse_location")
2. HTTP方法规范
| 操作 | HTTP方法 | URL模式 |
|---|
| 获取单条 | GET | /xxx |
| 获取列表 | GET | /xxx/list |
| 分页查询 | GET | /xxx/page |
| 创建 | POST | /xxx |
| 更新 | POST | /xxx/update |
| 删除 | POST | /xxx/delete |
3. Query参数规范
id: int = Query(..., description="ID")
name: Optional[str] = Query(None, description="名称")
page_index: int = Query(1, ge=1, description="页码")
page_size: int = Query(10, ge=1, le=100, description="每页数量")
分页查询规范
返回格式
return success_response({
"data": data,
"totals": totals,
"page_index": page_index,
"page_size": page_size
})
前端参数规范
| 参数名 | 类型 | 必填 | 说明 |
|---|
| page_index | int | 是 | 页码,从1开始 |
| page_size | int | 是 | 每页数量(建议10/20/50/100) |
Service层开发模式
标准CRUD方法返回值约定
id, msg = await service.add(view_model, current_user)
flag, msg = await service.update(id, view_model, current_user)
flag, msg = await service.delete(id, current_user)
result = await service.get_by_id(id, current_user)
result = await service.get_all(current_user)
关键:传递CurrentUser
所有涉及业务数据的Service方法都需要接收current_user参数,用于:
- 获取
tenant_id进行数据隔离
- 获取
user_id记录操作人
- 权限验证
async def get_by_id(self, id: int, current_user: CurrentUser):
return await self.get_one_by_tenant(self._repository._model, current_user.tenant_id, {"id": id})
路由注册
在main.py中注册
from app.api.v1.tenant import stock, warehouse, sku
def create_app():
app = FastAPI()
api_router = APIRouter()
api_router.include_router(stock.router, tags=["库存管理"])
api_router.include_router(warehouse.router, tags=["仓库管理"])
api_router.include_router(sku.router, tags=["SKU管理"])
app.include_router(api_router, prefix="/api/v1/tenant")
return app
最佳实践
1. 依赖注入优先
async def get_xxx(
service: XxxService = Depends(get_service_dependency(XxxService)),
current_user: CurrentUser = Depends(get_current_user)
):
return await service.get_by_id(id, current_user)
async def get_xxx_custom(
current_user: CurrentUser = Depends(get_current_user),
db: AsyncSession = Depends(get_db_by_tenant)
):
pass
2. 必填参数使用...
id: int = Query(..., description="ID")
view_model: XxxCreateViewModel
id: int = Query(None, description="ID")
3. 分页参数校验
page_index: int = Query(1, ge=1, description="页码")
page_size: int = Query(10, ge=1, le=100, description="每页数量")
4. 描述清晰完整
@router.get("/xxx")
async def get_xxx(
id: int = Query(..., description="XXX ID"),
id: int = Query(...),
):
5. 添加接口文档注释
@router.get("/xxx")
async def get_xxx(...):
"""
根据ID获取XXX信息
Args:
id: XXX ID
service: XXX服务
current_user: 当前用户
Returns:
XXX信息
"""
6. 响应模型注解
@router.get("/xxx", response_model=XxxViewModel)
@router.get("/xxx/list", response_model=List[XxxViewModel])
@router.get("/xxx/page")
常见错误模式(避免)
1. 缺少tenant_id过滤
result = await service.get_all()
result = await service.get_all(current_user)
2. 缺少认证依赖
async def get_xxx(id: int):
pass
async def get_xxx(
id: int,
current_user: CurrentUser = Depends(get_current_user)
):
pass
3. 错误响应格式不一致
return {"error": "错误信息"}
return error_response("错误信息")
from fastapi import HTTPException
raise HTTPException(status_code=404, detail="记录不存在")
4. 路径命名不一致
@router.post("/xxxUpdate")
@router.get("/getXxxList")
@router.post("/xxx/update")
@router.get("/xxx/list")
开发流程 Checklist