SQLAlchemy 的两套 API

什么是"纯 SQLAlchemy Core 风格(非 ORM Session 查询)"

你贴的这段话是在说:这两个 Mapper 类没有用 SQLAlchemy 的 ORM 方式 (定义 class User(Base) 然后 session.query(User).filter(...)),而是用了 SQLAlchemy Core 的方式------直接拼 SQL 表达式、直接执行。

一、先分清 SQLAlchemy 的两套 API

SQLAlchemy 其实有两套独立但可混用的体系:

SQLAlchemy Core SQLAlchemy ORM
核心对象 Table、Column、select()、insert()、update()、delete() Base、class User(Base)、session.query()、session.add()
思维模式 面向 SQL:你写的是 SQL 表达式,Python 帮你拼成 SQL 面向对象:你操作的是 Python 对象,ORM 帮你翻译成 SQL
结果 返回 Row(类似 namedtuple) 返回对象实例(User 对象)
状态管理 无 session 状态跟踪 有 identity map、脏检查、级联、延迟加载
关系 手写 join relationship()、backref 自动处理
典型代码 conn.execute(select(user_table).where(user_table.c.id == 1)) session.query(User).filter(User.id == 1).first()

注意: Core 是 ORM 的底层 。ORM 内部就是靠 Core 把对象操作翻译成 SQL 的。所以"用 Core"不是"绕过 SQLAlchemy",而是用了更底层、更贴近 SQL 的那一层。

二、两种风格代码对比

假设有张表 bsv_meta:

ORM 风格(Session 查询)

python 复制代码
# 1. 定义模型
class BsvMeta(Base):
    __tablename__ = "bsv_meta"
    id = Column(Integer, primary_key=True)
    name = Column(String(64))
    enabled = Column(Boolean, default=False)

# 2. 查询
def get_meta(meta_id: int):
    return db.session.query(BsvMeta).filter(BsvMeta.id == meta_id).first()
    # 返回 BsvMeta 对象,可以 meta.name、meta.enabled 访问

# 3. 插入
def create_meta(name: str):
    m = BsvMeta(name=name)
    db.session.add(m)
    db.session.commit()
    return m

Core 风格(非 ORM Session 查询)

python 复制代码
# 1. 定义表(不是类!)
bsv_meta = Table(
    "bsv_meta", metadata,
    Column("id", Integer, primary_key=True),
    Column("name", String(64)),
    Column("enabled", Boolean, default=False),
)

# 2. 查询
def get_meta(meta_id: int):
    stmt = select(bsv_meta).where(bsv_meta.c.id == meta_id)
    row = db.session.execute(stmt).first()
    # 返回 Row,用 row.id / row.name 或 row._mapping["name"] 访问

# 3. 插入
def create_meta(name: str):
    stmt = insert(bsv_meta).values(name=name)
    result = db.session.execute(stmt)
    db.session.commit()
    return result.inserted_primary_key

关键区别:

  • Core 里表是 Table 对象,字段通过 bsv_meta.c.id(.c = columns)访问。
  • 查询用 select(...)、insert(...) 这种函数式构造 SQL 语句 ,再 execute()。
  • 返回的是 Row(像带字段名的元组),不是 ORM 对象。

三、"非 ORM Session 查询"到底强调什么

这句话里的"非 ORM Session 查询"是在强调:

它虽然用了 db.session.execute()(借用了 session 来执行),但执行的是 Core 的 select() 语句,而不是 ORM 的 session.query(Model)。

session 在这里只是**"执行器 + 事务上下文"**,不承担 ORM 的"对象状态跟踪"职责。也就是说:

python 复制代码
# ORM 查询:session 会跟踪返回的对象、缓存、脏检查
session.query(User).filter(...).all()

# Core 查询:session 只是执行 SQL,返回原始 Row,不跟踪对象
session.execute(select(user_table).where(...)).all()

同一个 session,两种用法。 第二种就是"非 ORM Session 查询"。

四、为什么 RuoYi-Python 的 Mapper 要用 Core 风格

结合你贴的那段描述(571 行的 CRUD + 互斥启用 + 引用计数 + 分页拦截),用 Core 有几个现实理由:

理由 说明
贴近若依框架原设计 RuoYi 的 Mapper 层本来就是"写 SQL / 映射 SQL"的思路(Java 版是 XML 写 SQL)。Core 风格最接近这种"我在写 SQL"的心智模型。
复杂 SQL 更好控制 互斥启用(UPDATE ... SET enabled=0 WHERE type=X)、引用计数(子查询 COUNT(*))、时间范围筛选,用 Core 拼比 ORM 的 relationship 更直观、更可控。
不想建 ORM 模型 元数据表往往字段多、结构固定、不需要对象关系,定义 6 个 ORM Model 反而累赘。直接 Table + select 更轻。
性能可控 Core 少一层 ORM 对象构造和状态跟踪,批量操作、复杂 join 时开销更小、行为更可预测。
分页拦截器 你提到的 flask.g.criterian_meta.page.stmt 注入若依分页------分页拦截器通常要拿到原始 select 语句 去改写成 COUNT 和 LIMIT。Core 的 select() 语句对象天生适合被拦截改写;ORM 的 session.query() 也能做,但 Core 更直接。

五、一句话总结

"纯 SQLAlchemy Core 风格(非 ORM Session 查询)"= 用 Table + select()/insert()/update()/delete() 来构造和执行 SQL,返回 Row;而不是用 class Xxx(Base) + session.query(Xxx) 返回 ORM 对象。 session 只当执行器和事务用,不参与对象状态管理。

六、对你的实际影响

如果你要读懂或修改 BsvMetaMapper / MeasureRecordMapper,记住:

  1. 里面出现的字段访问是 表.c.字段(如 bsv_meta.c.name),不是 对象.字段。
  2. 返回值是 Row,用 row.name 或 row._mapping["name"] 取值;不能 row.name = "x" 改完就自动同步(那是 ORM 才有)。
  3. 想看它到底执行了什么 SQL,可以 print(stmt.compile(compile_kwargs={"literal_binds": True}))。
  4. 分页不靠 LIMIT 手写,而是把 stmt 挂到 flask.g.criterian_meta.page.stmt,由若依的拦截器统一改写。

如果你想,可以把 BsvMetaMapper 里一个具体方法贴出来,我帮你逐行翻译成它实际执行的 SQL,你就能彻底对上号。

相关推荐
把我格子衫拿来14 分钟前
鼠标点到哪里才算选中?Qt 与 VTK 的拾取坐标、DPI 与体素定位
python·qt·vtk·坐标变换·三维交互
泡茶喝茶写代码44 分钟前
A股量化策略回测实战:从行情数据到绩效评估(第 1 篇):回测引擎从零搭建
java·python·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口
RobinDevNotes1 小时前
SGLang 上手:重复的前缀只算一次
人工智能·python·深度学习·sglang·c4python
zyseo81 小时前
谷歌SEO 内链地图脚本:Python 画出权重分发,揪出孤岛页
java·前端·python
破无差1 小时前
精武会徽turtle实现
python
y = xⁿ1 小时前
拆解cloud code记忆系统
java·人工智能·python·github
zzu123zsw1 小时前
打破单模型幻觉与信息茧房:24k Star 开源 BettaFish 的多智能体辩论拓扑与实时舆情研判实战
python
csdn_zhangchunfeng2 小时前
linux 如何安装rst2man命令
开发语言·chrome·python
滴啦嘟啦哒2 小时前
【具身笔记】【π0.5 微调与仿真】三、libero数据可视化
笔记·python·ros·vla
浩腾数字多媒体2 小时前
展厅中控源头厂家怎么选要看哪些核心指标?
网络·python