Skip to content

CRUD 操作

继承 TableBaseMixin(自增 ID)或 UUIDTableBaseMixin(UUID ID)即可获得全套异步 CRUD 方法。

工具函数

rel() — 类型安全的关系引用

basedpyright 会把 SQLModel 的 Relationship 字段推断为注解类型(如 LLM),而非 QueryableAttributerel() 解决这个类型问题:

python
from sqlmodel_ext import rel

# 不用 rel:basedpyright 报类型错误
user = await User.get(session, load=User.profile) 

# 用 rel:类型正确
user = await User.get(session, load=rel(User.profile)) 

cond() — 类型安全的条件组合

类似问题:basedpyright 把 Model.field == value 推断为 bool,导致 & / | 运算符报错。

python
from sqlmodel_ext import cond

scope = cond(UserFile.user_id == current_user.id) 
condition = scope & cond(UserFile.status == FileStatusEnum.uploaded) 
users = await UserFile.get(session, condition, fetch_mode="all")

sanitize_integrity_error() — 友好化错误消息

IntegrityError 中提取用户安全的错误消息。特别支持 PostgreSQL 触发器的 SQLSTATE 23514 错误:

python
from sqlalchemy.exc import IntegrityError

try:
    await order.save(session)
except IntegrityError as e:
    msg = Order.sanitize_integrity_error(e, "操作失败")
    # PostgreSQL 触发器消息会被提取;其他约束错误返回默认消息

内置字段

继承后自动获得三个字段:

字段类型说明
idintUUID主键,自动生成
created_atdatetime创建时间,自动设置
updated_atdatetime更新时间,每次 UPDATE 自动刷新

save() — 创建或更新

python
user = User(name="Alice", email="alice@example.com")
await user.save(session)            # 禁止这么写,会导致过期
user = await user.save(session)     # 正确写法
print(user.id)  # 已有值

# 修改后再次 save = UPDATE
user.name = "Bob"
user = await user.save(session)

参数

参数默认值说明
commitTrueFalse 时只 flush 不 commit,适合批量操作
loadNone保存后预加载指定关系
optimistic_retry_count0乐观锁冲突时的重试次数
python
# 批量操作:前面只 flush,最后一个 commit
await user1.save(session, commit=False)
await user2.save(session, commit=False)
user3 = await user3.save(session)  # 一次性 commit 全部

# 保存后预加载关系
user = await user.save(session, load=User.profile)

add() — 批量插入

python
users = [User(name="Alice"), User(name="Bob")]
users = await User.add(session, users)

# 单条也行
user = await User.add(session, User(name="Eve"))

update() — 局部更新(PATCH 语义)

python
class UserUpdate(SQLModelBase):
    name: str | None = None
    email: str | None = None

data = UserUpdate(name="Bob")  # 只设置了 name
user = await user.update(session, data)
# 只更新 name,email 保持不变(exclude_unset=True)

参数

参数默认值说明
exclude_unsetTrue只更新显式设置的字段(PATCH 语义)
excludeNone排除某些字段不允许更新
extra_dataNone在 update 模型之外追加额外字段
python
# 追加额外字段
user = await user.update(session, data, extra_data={"updated_by": admin.id})

# 排除敏感字段
user = await user.update(session, data, exclude={"role", "is_admin"})

delete() — 删除

两种模式,互斥使用:

python
# 按实例删除
await User.delete(session, user)
await User.delete(session, [user1, user2])

# 按条件批量删除(⚠️ 将删除所有匹配记录)
count = await User.delete(session, condition=User.is_active == False) 

返回值为删除的记录数。

get() — 查询

万能查询方法,通过不同参数组合覆盖所有查询场景。

基本查询

python
# 按条件查第一条(默认 fetch_mode="first")
user = await User.get(session, User.email == "alice@example.com")

# 查所有
users = await User.get(session, fetch_mode="all") 

# 精确查一条(0 条或多条都报错)
user = await User.get(session, User.id == some_id, fetch_mode="one") 

fetch_mode 返回值

fetch_mode返回类型0 条时多条时
"first"(默认)T | NoneNone返回第一条
"one"T抛异常抛异常
"all"list[T]空列表全部返回

分页与排序

python
from sqlmodel_ext import TableViewRequest

tv = TableViewRequest(offset=0, limit=20, desc=True, order="created_at")
users = await User.get(session, fetch_mode="all", table_view=tv)
# → SELECT ... ORDER BY created_at DESC LIMIT 20 OFFSET 0

时间过滤

python
users = await User.get(
    session,
    fetch_mode="all",
    created_after=datetime(2024, 1, 1),
    created_before=datetime(2024, 12, 31),
)

关系预加载

python
user = await User.get(
    session,
    User.id == user_id,
    load=[User.profile, Profile.avatar], 
)
# 自动构建: selectinload(User.profile).selectinload(Profile.avatar)

其他参数

参数作用
joinJOIN 另一张表
options自定义 SQLAlchemy query options
filter额外的 WHERE 条件
with_for_updateSELECT ... FOR UPDATE(行锁)

count() — 计数

python
total = await User.count(session)
active = await User.count(session, User.is_active == True)

get_with_count() — 分页列表

count() + get(fetch_mode="all") 的组合,返回 ListResponse

python
result = await User.get_with_count(session, table_view=table_view)
# result.count = 42
# result.items = [User(...), User(...), ...]

get_one() — 保证存在的查询

类似 get(fetch_mode="one"),但接口更简洁——直接传 ID:

python
user = await User.get_one(session, user_id)
# 记录不存在 → NoResultFound
# 多条记录 → MultipleResultsFound

# 带锁查询
user = await User.get_one(session, user_id, with_for_update=True)

get_one() vs get_exist_one()

get_one() 抛 SQLAlchemy 异常(NoResultFound),get_exist_one() 抛 HTTP 404(有 FastAPI 时)或 RecordNotFoundError

get_exist_one() — 查找或 404

python
user = await User.get_exist_one(session, user_id) 
# 找不到自动抛 HTTPException(404)(有 FastAPI 时)
# 或 RecordNotFoundError(无 FastAPI 时)

# 带关系预加载
user = await User.get_exist_one(session, user_id, load=User.profile)

方法速查表

方法类型对应 SQL返回值
add()类方法INSERT实例或列表
save()实例方法INSERT 或 UPDATE刷新后的实例
update()实例方法UPDATE(部分字段)刷新后的实例
delete()类方法DELETE删除数量
get()类方法SELECT + WHERE + ...实例 / 列表 / None
get_one()类方法SELECT + 保证存在实例
count()类方法SELECT COUNT(*)int
get_with_count()类方法COUNT + SELECTListResponse
get_exist_one()类方法SELECT + 404实例