You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.7 KiB
6.7 KiB
修复文件夹 API Schema 驼峰命名支持
日期: 2026-01-29
类型: Bug 修复
影响范围: 文件夹 API、Schema 定义
问题描述
文件夹 API 测试失败,返回 400 错误:
POST /api/v1/folders completed with status 400
根本原因
-
Schema 字段命名不一致:
- 前端/测试使用驼峰命名:
folderCategory,parentFolderId,coverImageId - 后端 Schema 定义使用蛇形命名:
folder_category,parent_folder_id,cover_image_id - Pydantic 没有配置
alias和populate_by_name,导致无法识别驼峰命名
- 前端/测试使用驼峰命名:
-
API 响应序列化错误:
- UUID 对象未转换为字符串
- 缺少
folderCategoryName字段
解决方案
1. 修复 Schema 定义
为所有文件夹相关 Schema 添加 alias 和 populate_by_name 配置:
FolderCreate
class FolderCreate(BaseModel):
"""创建文件夹请求"""
name: str = Field(..., min_length=1, max_length=255, description="文件夹名称")
description: Optional[str] = Field(None, description="文件夹描述")
parent_folder_id: Optional[UUID] = Field(None, alias="parentFolderId", description="父文件夹ID")
folder_category: Optional[int] = Field(
None,
alias="folderCategory",
ge=1,
le=2,
description="文件夹分类: 1=我的项目, 2=协作项目"
)
color: Optional[str] = Field(
None,
pattern=r"^#[0-9A-Fa-f]{6}$",
description="文件夹颜色(十六进制)"
)
icon: Optional[str] = Field(None, max_length=50, description="文件夹图标")
cover_image_id: Optional[UUID] = Field(None, alias="coverImageId", description="封面图片ID")
model_config = {
"populate_by_name": True # 支持驼峰和蛇形命名
}
FolderUpdate
class FolderUpdate(BaseModel):
"""更新文件夹请求"""
name: Optional[str] = Field(None, min_length=1, max_length=255)
description: Optional[str] = None
color: Optional[str] = Field(None, pattern=r"^#[0-9A-Fa-f]{6}$")
icon: Optional[str] = Field(None, max_length=50)
cover_image_id: Optional[UUID] = Field(None, alias="coverImageId")
sort_order: Optional[int] = Field(None, alias="sortOrder")
model_config = {
"populate_by_name": True
}
FolderMove
class FolderMove(BaseModel):
"""移动文件夹请求"""
parent_folder_id: Optional[UUID] = Field(None, alias="parentFolderId", description="目标父文件夹ID")
model_config = {
"populate_by_name": True
}
2. 修复 API 响应
修改 POST /api/v1/folders 端点,确保:
- UUID 转换为字符串
- 添加
folderCategoryName字段
@router.post("", response_model=ApiResponse[FolderResponse], summary="创建文件夹")
async def create_folder(
folder_data: FolderCreate,
current_user: User = Depends(get_current_user),
session: AsyncSession = Depends(get_session)
):
service = FolderService(session)
folder = await service.create_folder(current_user.user_id, folder_data)
return success_response(data={
"id": str(folder.id), # UUID → 字符串
"name": folder.name,
"description": folder.description,
"parentFolderId": str(folder.parent_folder_id) if folder.parent_folder_id else None,
"ownerId": str(folder.owner_id), # UUID → 字符串
"path": folder.path,
"level": folder.level,
"folderCategory": folder.folder_category,
"folderCategoryName": FolderCategory.get_display_name(folder.folder_category), # 新增
"sortOrder": folder.sort_order,
"color": folder.color,
"icon": folder.icon,
"isShared": folder.is_shared,
"createdAt": folder.created_at,
"updatedAt": folder.updated_at,
})
3. 添加导入
from app.models.folder import FolderCategory
修改文件
修改文件
-
server/app/schemas/folder.py- 为
FolderCreate,FolderUpdate,FolderMove添加alias和model_config
- 为
-
server/app/api/v1/folders.py- 修复
create_folder响应序列化 - 添加
FolderCategory导入
- 修复
验证结果
测试结果
$ docker exec jointo-server-app pytest tests/integration/test_folder_api.py::TestFolderCRUD::test_create_root_folder -v
PASSED ✅
API 请求示例
请求(驼峰命名):
POST /api/v1/folders
{
"name": "我的项目",
"description": "根文件夹",
"folderCategory": 1
}
响应:
{
"code": 200,
"message": "Success",
"data": {
"id": "019c07ec-72a7-7152-a888-cfe3159ce317",
"name": "我的项目",
"description": "根文件夹",
"parentFolderId": null,
"ownerId": "019c07eb-70d8-7191-be3e-ba0e6099ef70",
"path": "/我的项目",
"level": 0,
"folderCategory": 1,
"folderCategoryName": "我的项目",
"sortOrder": 0,
"color": null,
"icon": null,
"isShared": false,
"createdAt": "2026-01-29T04:04:22.350207+00:00",
"updatedAt": "2026-01-29T04:04:22.350214+00:00"
}
}
影响
正面影响
- ✅ 支持前端使用驼峰命名调用 API
- ✅ 保持后端代码使用 Python 蛇形命名规范
- ✅ 提高 API 的易用性和一致性
API 兼容性
- ✅ 同时支持驼峰和蛇形命名(
populate_by_name=True) - ✅ 向后兼容,不影响现有代码
最佳实践
Pydantic Schema 命名规范
-
字段定义使用蛇形命名(Python 规范):
folder_category: Optional[int] parent_folder_id: Optional[UUID] -
添加驼峰命名 alias(前端友好):
folder_category: Optional[int] = Field(None, alias="folderCategory") parent_folder_id: Optional[UUID] = Field(None, alias="parentFolderId") -
启用双向命名支持:
model_config = { "populate_by_name": True # 同时接受 alias 和原始字段名 }
API 响应规范
-
UUID 必须转换为字符串:
"id": str(folder.id) -
枚举值提供显示名称:
"folderCategory": folder.folder_category, "folderCategoryName": FolderCategory.get_display_name(folder.folder_category)
相关文档
后续工作
- 为其他文件夹 API 端点添加相同的响应格式
- 补充缺失的测试 fixture(
test_folder_id,test_other_user_id等) - 实现完整的文件夹 CRUD、移动、共享、导出功能
- 运行完整的集成测试套件