第十五章 数据库操作

篇别:第三篇 数据库与数据管理

本章学习目标

  • 能在确需时 安全地 使用原生 SQL(参数化),并理解 绕过 ORM 对权限与缓存的影响。
  • 了解 odoo.tools.SQL(或当前版本等价 API)组合 SQL 的方式,避免注入。
  • 能规划 模块迁移脚本大表变更 的风险控制。
  • 能执行 备份/恢复(含 filestore)并理解 RPO/RTO 基本概念。

导读:ORM 优先,SQL 慎用

Odoo 日常开发应 优先 ORM:自动处理 权限、多公司、翻译字段、审计。原生 SQL 适用于:重型报表、批量维护、与外部 DBA 协作的优化。一旦手写 SQL,你必须 自行保证 访问控制与安全。


15.1 SQL 查询

15.1.1 知识要点

  • self.env.cr(或 self._cr 已废弃):游标与当前事务绑定。
  • execute(query, params)务必使用 %s 占位符 + 元组参数,禁止 % name 类字符串拼接。
  • 只读查询:长报表可考虑 READ ONLY 游标或副本库(PG 特性,视配置而定)。
  • 返回cr.fetchall() / dictfetchall()(若可用)等。

15.1.2 案例

案例 A:安全查询

self.env.cr.execute(
    "SELECT id, name FROM library_book WHERE isbn = %s",
    (isbn,),
)
rows = self.env.cr.fetchall()

案例 B:危险写法(禁止)

# 禁止
self.env.cr.execute("SELECT id FROM library_book WHERE name = '%s'" % name)

案例 C:ORM 与 SQL 混用

先 SQL 取 id 列表,再 self.env['library.book'].browse(ids) 做后续 ORM 操作(注意 browse 会校验权限,若用 sudo 则须自律)。

15.1.3 截图占位

图 15-1 psql / pgAdmin 中对同一条 SQL 执行 EXPLAIN ANALYZE

15.1.4 本节练习

  1. 改错:解释 "WHERE name = '%s'" % namename' 时会发生什么。
  2. 实操:对 library_book 上一句按 isbn 查询的 EXPLAIN,判断是否走索引。
  3. 简答:手写 SQL 时 记录规则 是否自动生效?

参考答案提示:1. 注入或语法错误。3. 不生效,须自行叠加条件或改用 ORM。


15.2 SQL Wrapper(补充)

15.2.1 知识要点

Odoo 提供 SQL 等工具类(包路径与 API 以 19.0 文档与源码 为准),用于:

  • 拼接 WHERE 子句片段仍保持 绑定参数
  • 标识符引用 Identifier 防止误拼表名;
  • Domain 转 SQL 等内部逻辑中与 ORM 一致化。

15.2.2 案例(概念性,具体 import 以版本为准)

from odoo.tools import SQL

# 伪代码示意:实际构造方式见官方文档
# sql = SQL("SELECT id FROM library_book WHERE isbn = %s", isbn)
# self.env.cr.execute(sql)

15.2.3 截图占位

图 15-2 官方文档或源码 SQL 工具类

15.2.4 本节练习

  1. 实操:找一段项目中 字符串拼接 的 SQL,改写为 参数化(或 SQL 对象)。
  2. 简答SQL 对象相对裸字符串的 团队维护 优势?

15.3 数据库迁移

15.3.1 知识要点

  • Odoo 模块升级-u module 触发 模型同步、视图更新、XML/CSV 重载(受 noupdate 影响)。
  • 版本迁移脚本migrations/<version>/pre/post 脚本(命名与执行机制以 upgrade 工具 / Odoo 版本 为准)。
  • 大表 DDLADD COLUMN / CREATE INDEX CONCURRENTLY(PG)等需 锁与时长 评估;优先在 低峰维护窗 执行。
  • 流程备份 → 副本验证 → 生产执行 → 验证回滚路径

15.3.2 案例

pre-migrate:在 ORM 改表结构 做数据清洗(如拆分列、填默认值)。

post-migrate:结构已就绪后 批量 UPDATE、重建统计信息。

15.3.3 截图占位

图 15-3 升级日志含 migrate 脚本输出

15.3.4 本节练习

  1. 简答:pre-migrate 与 post-migrate 的典型分工各举一例。
  2. 情景:表 5000 万行NOT NULL 列,直接 UPDATE 全表有何风险?
  3. 判断:所有数据修复都应在 SQL 迁移脚本完成。( )

参考答案提示:2. 长锁、磁盘、回滚困难。3. 错,可用 ORM、服务器动作、分批任务等。


15.4 数据备份与恢复

15.4.1 知识要点

  • pg_dump:逻辑备份;-Fc 自定义格式便于 pg_restore 选择性恢复。
  • filestoreir.attachment 等二进制可能存 文件系统;备份必须 DB + filestore 一致(或明确仅用 DB 内存储策略)。
  • Odoo 命令db.dump / db.restore(以 CLI 文档为准)封装常见操作。
  • RPO:最多丢多久数据;RTO:多久恢复服务。

15.4.2 案例

# 示例:自定义格式备份
pg_dump -h localhost -U odoo -Fc mydb > mydb_$(date +%Y%m%d).dump

# 恢复到新库名
createdb mydb_restored
pg_restore -d mydb_restored mydb_20260329.dump

filestore 同步(示意):

rsync -av /var/lib/odoo/filestore/mydb/ /backup/filestore/mydb/

15.4.3 截图占位

图 15-4 cron 或面板中的备份任务

图 15-4b filestore 目录与数据库名对应关系

15.4.4 本节练习

  1. 实操:在 测试机 从 dump 恢复库并 登录验证 附件图片是否可读。
  2. 简答:仅恢复 DB、不恢复 filestore 会导致什么现象?
  3. 简答每日全量 + 连续归档(WAL) 对 RPO 的意义?

15.5 只读查询 API(了解)

15.5.1 知识要点

较新版本提供 execute_query 一类 只读 SQL 入口(名称与签名以文档为准),在 受限上下文 中返回字典行,用于报表与分析;仍须注意 权限与数据范围由业务封装。

15.5.2 本节练习

  1. 拓展:在官方文档搜索 execute_query,记录其与普通 cr.execute 的差异一句。

本章综合练习

  1. 事故演练:误执行 DELETE1 万行——写出 恢复步骤(从备份时间点、filestore、业务校验到沟通)。
  2. 选型:同一聚合报表,read_group手写 SQL GROUP BY 的取舍表(权限、性能、可维护性三行)。
  3. 设计:为「月度借阅统计」写 ORM 方案SQL 方案 提纲各一条。
  4. 合规:备份文件含 个人数据 时的 加密与访问控制 要点(简答)。

本章对应白皮书目录:第十五章 数据库操作。