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.
3.7 KiB
3.7 KiB
RFC 122: 修复 Project 模型外键引用错误
状态: ✅ 已完成
创建时间: 2026-01-20
作者: System
类型: Bug 修复
问题描述
用户登录时遇到 500 错误和 CORS 问题,根本原因是:
- 数据库模型错误:
ProjectMember、ProjectShare、ProjectVersion表的外键引用了不存在的列名projects.project_id - CORS 配置问题:
.env中的CORS_ORIGINS字符串格式无法被 Pydantic 正确解析为列表
错误日志
sqlalchemy.exc.InvalidRequestError: Could not initialize target column for ForeignKey
'projects.project_id' on table 'project_members': table 'projects' has no column named 'project_id'
根本原因
Project 模型使用了 alias="project_id",但这只是 API 层面的别名,数据库实际列名仍是 id。外键定义错误地引用了别名而非实际列名。
解决方案
1. 修复外键引用
将所有外键从 foreign_key="projects.project_id" 改为 foreign_key="projects.id":
修改文件: server/app/models/project.py
# ProjectMember
project_id: UUID = Field(foreign_key="projects.id", index=True)
# ProjectShare
project_id: UUID = Field(foreign_key="projects.id", index=True)
# ProjectVersion
project_id: UUID = Field(foreign_key="projects.id", index=True)
2. 修复 CORS 配置解析
修改文件: server/app/core/config.py
class Settings(BaseSettings):
# CORS 配置
CORS_ORIGINS: str = '["http://localhost:6160"]'
@property
def cors_origins_list(self) -> List[str]:
"""解析 CORS_ORIGINS 为列表"""
import json
if isinstance(self.CORS_ORIGINS, str):
try:
return json.loads(self.CORS_ORIGINS)
except json.JSONDecodeError:
return [self.CORS_ORIGINS]
return self.CORS_ORIGINS if isinstance(self.CORS_ORIGINS, list) else [self.CORS_ORIGINS]
修改文件: server/app/main.py
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins_list, # 使用新属性
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
验证结果
1. 应用启动成功
✅ Database initialized
INFO: Application startup complete.
2. 登录接口正常
curl -X POST http://192.168.31.178:6170/api/v1/auth/login/phone \
-H "Content-Type: application/json" \
-d '{"phone":"13800138000","country_code":"+86","code":"6666"}'
# 返回 200 OK,包含正确的 CORS 头
access-control-allow-origin: http://192.168.31.178:6160
技术要点
SQLModel 字段别名 vs 数据库列名
Field(alias="xxx")只影响 API 序列化/反序列化- 数据库列名由字段名决定(除非使用
sa_column=Column(name="xxx")) - 外键引用必须使用实际数据库列名
Pydantic 环境变量解析
- 复杂类型(List、Dict)在
.env中需要 JSON 字符串格式 - 使用
@property提供灵活的类型转换 - 支持字符串和列表两种输入格式
影响范围
- ✅ 修复用户登录功能
- ✅ 修复 CORS 跨域问题
- ✅ 修复项目相关表的外键约束
- ✅ 无需数据迁移(表结构未变)
后续建议
- 统一命名规范:避免在模型中使用
alias,保持字段名与数据库列名一致 - 配置验证:在应用启动时验证 CORS 配置是否正确解析
- 测试覆盖:添加模型关系的集成测试,避免类似问题