sqlmodel中应优先用field定义模型,它自动桥接pydantic验证与sqlalchemy列类型;fieldinfo仅用于底层定制。混用column会忽略field参数,relationship需显式配置lazy策略才能序列化关联数据。

SQLModel 模型定义时,Field 和 FieldInfo 到底怎么选?
SQLModel 的核心混淆点就在这儿:它把 Pydantic 的 Field(来自 pydantic.v1 或 pydantic v2)和 SQLAlchemy 的列定义混在了一起。你写 name: str = Field(..., max_length=50),看起来像 Pydantic 验证,其实 max_length 会被自动转成 String(50) —— 但仅当字段类型是 str 且没显式用 Column 包裹时才生效。
- 如果你用了
Column(String(50)),Field(max_length=50)就被完全忽略,Pydantic 验证也不触发 -
Field(default=None)和Field(default_factory=lambda: uuid4())是安全的,会同时影响 ORM 默认值和 Pydantic 初始化逻辑 -
Field(exclude=True)只作用于 Pydantic 序列化(比如.model_dump()),对数据库字段无影响
from sqlmodel import SQLModel, Field from typing import Optional <p>class User(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) name: str = Field(..., max_length=50) # ✅ 自动生成 String(50),且校验长度 email: str = Field(..., sa_column=Column(String(100))) # ❌ max_length 失效,只认 Column 定义</p>
查询结果转 Pydantic 模型时,为什么 .model_dump() 报 AttributeError: 'User' object has no attribute '__pydantic_core_schema__'?
这不是你的模型写错了,而是你混用了 SQLModel 模型和纯 Pydantic 模型的序列化路径。SQLModel 类本身继承自 Pydantic BaseModel,但它在 v0.0.19+ 后默认启用 pydantic.v2,而老项目可能还卡在 v1 兼容模式;更常见的是:你从数据库查出来的对象是 ORM 实例,带延迟加载属性(比如 user.posts),直接调 .model_dump() 会尝试序列化未加载的关联字段,触发 lazy loading 异常,然后错误被掩盖成上面那个 schema 错误。
- 确保 SQLModel 版本 ≥ 0.0.19,并显式指定
pydantic_version="2"(如果手动控制) - 查询后立刻用
sqlmodel.sqlmodel_session.expunge_all()或session.refresh(obj)清理状态,避免延迟加载干扰 - 更稳妥的做法:用
model_validate(obj.<strong>dict</strong>)(v2)或parse_obj(obj.<strong>dict</strong>)(v1)绕过 ORM 属性代理机制
别直接对 ORM 实例调 .model_dump(mode="json"),尤其当模型含 Relationship 时——它大概率会崩。
Relationship 字段在 Pydantic 输出里总是 None,即使数据库有数据
SQLModel 的 Relationship 默认不自动加载关联数据,它只是声明“这儿有个外键关系”,行为上等价于 SQLAlchemy 的 relationship(lazy="selectin") 需要你主动配置。所以哪怕数据库里 user.profile_id 不为空,user.profile 在 Pydantic dump 里也还是 None,因为 ORM 根本没去查那张表。
本文档主要讲述的是Python开发网站指南;HTML是网络的通用语言,一种简单、通用的全置标记语言。它允许网页制作人建立文本与图片相结合的复杂页面,这些页面可以被网上任何其他人浏览到,无论使用的是什么类型的电脑或浏览器 Python和其他程序语言一样,有自身的一套流程控制语句,而且这些语句的语法和其它程序语言类似,都有for, if ,while 类的关键字来表达程序流程。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
立即学习“Python免费学习笔记(深入)”;
- 在
Relationship中显式加sa_relationship_kwargs={"lazy": "joined"}(适合一对一)或{"lazy": "selectin"}(一对多) - 如果只想在序列化时包含关联,别依赖
Relationship自动填充,改用model_computed_field+@computed_field手动拼装(v2),或者用model_config = {"include": {"profile": True}}配合预加载 - 注意:加了
lazy="joined"会导致每次查 User 都JOINProfile 表,N+1 问题换成了笛卡尔积风险
Relationship 不是“自动关联”,它只是个声明。想输出关联数据,得自己告诉 SQLModel “现在就要查”。
用 create_engine 连接 PostgreSQL 时,pool_pre_ping=True 为什么让 FastAPI 接口变慢?
pool_pre_ping=True 的本意是每次取连接前发一个 SELECT 1 检查连通性,防止用到已断开的连接。但它在高并发下会造成明显延迟,尤其当数据库负载高、网络抖动或连接池空闲连接多时,每个请求都多一次 round-trip。
- 生产环境更推荐
pool_recycle=3600(一小时强制重连)+pool_pre_ping=False,再配合监控告警(如 pg_stat_activity 中 idle in transaction 超时) - 如果必须用
pre_ping,至少加上pool_use_lifo=True,让连接池优先复用刚释放的连接(减少新连接建立概率) - FastAPI 的
Depends中每次新建 session?那pre_ping会每请求触发一次——应该用单例 engine + request-scoped session,而不是 per-request engine
pre_ping 是防错手段,不是性能方案。线上压测时关掉它,看错误率是否真升高;如果没升,说明网络和 DB 稳定,没必要为小概率问题牺牲确定性延迟。









