
SQLModel 在设置 table=True 时会禁用 Pydantic v2 的字段验证器(如 @field_validator),导致 mode="before" 等验证逻辑完全跳过,数据未经序列化即传入数据库,引发类型错误。
sqlmodel 在设置 `table=true` 时会禁用 pydantic v2 的字段验证器(如 `@field_validator`),导致 `mode="before"` 等验证逻辑完全跳过,数据未经序列化即传入数据库,引发类型错误。
在 SQLModel 中,当你声明一个模型并设置 table=True(例如用于 ORM 映射的数据库表模型),该模型虽继承自 pydantic.BaseModel,但其初始化路径会绕过标准的 Pydantic 验证流程——字段验证器(@field_validator)、根验证器(@model_validator)及 mode="before"/"after" 均不会被调用。这是 SQLModel 为兼容 SQLAlchemy 构建机制而做出的设计妥协,早在 2022 年的 GitHub Issue #453 中就被明确记录,并持续存在于当前版本(如 sqlmodel 0.0.23 + pydantic 2.10.6)。
✅ 正确解决方案:使用 @sqlmodel.root_validator(已弃用)或改用 model_validate + 自定义构造
由于 @field_validator 在 table=True 下失效,推荐以下两种稳健实践:
方案一:显式调用 model_validate()(推荐,Pydantic v2 风格)
将请求体先用纯 Pydantic 模型(非 table=True)校验和转换,再转为 SQLModel 实例:
from typing import Optional, Dict, Any
import json
import sqlmodel
import pydantic
from fastapi import FastAPI, Depends
# 1. 定义纯 Pydantic 模型用于输入校验(无 table=True)
class FooInput(pydantic.BaseModel):
data: str | Dict[str, Any]
@pydantic.field_validator("data", mode="before")
def serialize_dict(cls, value):
if isinstance(value, dict):
return json.dumps(value)
return value
# 2. SQLModel 表模型(仅用于数据库操作,不参与验证)
class Foo(sqlmodel.SQLModel, table=True):
id: Optional[int] = sqlmodel.Field(primary_key=True)
data: str
# FastAPI 路由
@app.post("/foos")
def create_foo(
payload: FooInput, # ✅ 先经 Pydantic 验证与转换
session: sqlmodel.Session = Depends(get_session)
):
# ✅ 手动构建 SQLModel 实例(data 已是 str)
foo = Foo(data=payload.data)
session.add(foo)
session.commit()
session.refresh(foo)
return foo方案二:重写 __init__(慎用,破坏 ORM 可预测性)
仅作了解,不推荐生产使用:
class Foo(sqlmodel.SQLModel, table=True):
id: Optional[int] = sqlmodel.Field(primary_key=True)
data: str
def __init__(self, **kwargs):
# 手动触发预处理(需自行保证幂等性和类型安全)
if "data" in kwargs and isinstance(kwargs["data"], dict):
kwargs["data"] = json.dumps(kwargs["data"])
super().__init__(**kwargs)⚠️ 注意事项
- 不要依赖
@field_validator或@model_validator(mode="before")在table=True模型上工作——它们静默失效,无报错、无日志; -
sqlmodel.SQLModel的验证行为与pydantic.BaseModel不等价;区分「输入模型」(Pydantic-only)和「存储模型」(SQLModel-only)是最佳实践; - 若需复杂业务逻辑(如嵌套结构自动序列化/反序列化),建议封装为
@staticmethod工具方法或使用sqlmodel.Field(default_factory=...)配合属性访问器(@property)。
总之,SQLModel 的核心定位是「ORM 与 Pydantic 的桥梁」,而非「全功能 Pydantic v2 ORM」。理解其验证边界,合理分层建模,才能避免隐式失败。

















