第一章 概念总论
篇别:第一篇 基础知识
本章学习目标
- 能说明 Odoo 模块、模型、Mixin 的区别与典型用途。
- 能读懂并编写简单的 domain、context,并理解视图修饰与权限的关系。
- 能列举 向导、定时任务、Discuss 相关模型 在业务中的常见用法。
- 能独立维护一份合理的
__manifest__.py,并解释 Odoo 19 架构层面的主要变化。
本章对 Odoo 19 的核心概念进行全景式梳理,帮助开发者建立系统化的认知框架。
1.1 模块(Module)概念
1.1.1 知识要点
Odoo 功能以 模块 为单位分发。每个模块是一个 Python 包,根目录至少包含:
__manifest__.py:模块清单(元数据、依赖、数据文件列表等);__init__.py:包初始化,通常from . import models等。
模型类型速查:
| 类型 | 基类 | 是否建表 | 典型用途 |
|---|---|---|---|
| 普通模型 | models.Model |
是(默认) | 订单、产品、客户等业务实体 |
| 抽象模型 | models.AbstractModel + _abstract = True |
否 | 字段/方法复用,不被直接实例化 |
| 临时模型 | models.TransientModel |
是(临时表,会清理) | 向导、一次性配置 |
| Mixin | 多为抽象模型,通过 _inherit 拼到业务模型上 |
否(Mixin 自身) | 消息、活动、阶段条等可插拔能力 |
历史上文档中的 BaseModel 泛指模型基类能力;NumericBrowse 等旧称可忽略,以 models.Model 与记录集 API 为准。
1.1.2 案例
案例 A:最小业务模块目录
my_library/
__init__.py
__manifest__.py
models/
__init__.py
book.py # models.Model → 图书记录,持久化
wizards/
borrow_wizard.py # models.TransientModel → 借书向导
案例 B:Mixin 的典型组合
class LibraryLoan(models.Model):
_name = 'library.loan'
_inherit = ['mail.thread', 'mail.activity.mixin']
# 获得 chatter、关注者、待办活动,而无需复制 mail 模块源码
1.1.3 截图占位

拍摄说明:在 IDE 或文件管理器中展开一个真实自定义模块根目录。
1.1.4 本节练习
- 判断题:
TransientModel的数据会永久保留在同一数据库中供审计使用。( ) - 简答题:说明
AbstractModel与「用_inherit拼到res.partner上的 mail.thread」在「是否单独建表」上的区别。 - 实操题:新建模块
demo_concept,内含一个持久化模型demo.concept.item(仅name字段)与一个向导模型,写出__init__.py与models包的最小导入链。
参考答案提示:1. 错(会定期清理)。2. AbstractModel 不建业务表;mail.thread 通过继承扩展已有表结构。3. 见 1.1.2 目录结构与第二章模型定义。
1.2 表达式与配置
1.2.1 知识要点
- domain:列表形式的查询条件。示例:
[('state', '=', 'draft')];前缀&、|、!组合逻辑。 - context:字典,影响默认值、语言、公司、
active_id等;代码中用record.with_context(lang='zh_CN', default_type='x')。 - attrs / states:旧式表单中控制
invisible、readonly、required;新版本逐步由 OWL + 视图属性 承接,维护老模块时仍会见到。 - groups:字段、按钮、菜单上的 可见性 门槛,值为用户组 XML ID。
- 视图层 invisible/readonly/required:仅影响界面时,不能替代 ACL;RPC 仍可能读写,须后端约束与权限配合。
1.2.2 案例
案例 A:动作里带 domain 与 context
<record id="action_loan_draft" model="ir.actions.act_window">
<field name="name">草稿借阅单</field>
<field name="res_model">library.loan</field>
<field name="domain">[('state','=','draft')]</field>
<field name="context">{'default_state': 'draft', 'search_default_my': 1}</field>
</record>
案例 B:Python 中临时切换上下文
records.with_context(active_test=False).search([])
# 常见:需要包含 archive 的记录时
1.2.3 截图占位

1.2.4 本节练习
- 填空:domain 中「与」的前缀操作符是 __。
- 改错题:「菜单设置了
groups,所以普通用户绝不可能通过 RPC 访问该模型。」错在哪里? - 实操题:写一个 domain,表示「状态为
done或cancel,且 partner_id 为当前表单上的partner_id」(提示:用变量时需在 Python 里拼 domain,XML 里用user_id等安全表达式)。
参考答案提示:1. &。2. 菜单只藏入口,ACL/记录规则才是模型访问边界。3. 如 [('state', 'in', ['done', 'cancel']), ('partner_id', '=', partner_id)](在方法内组装)。
1.3 核心组件
1.3.1 知识要点
- Wizard:
TransientModel+ 表单视图,完成导入、确认、批量操作等 一次性 流程。 - Mixin:可复用模型片段(消息线程、活动、评级等)。
- Cron:
ir.cron按间隔调用模型方法,适合同步、清理、提醒。 - mail.thread / mail.activity.mixin / mail.followers:Discuss 体系的核心;关注者决定 谁收到通知。
1.3.2 案例
案例 A:向导返回窗口动作
def action_apply(self):
self.ensure_one()
# ... 业务处理 ...
return {
'type': 'ir.actions.act_window',
'res_model': 'library.loan',
'view_mode': 'list,form',
'domain': [('id', 'in', self.loan_ids.ids)],
}
案例 B:Cron 调用模型方法
在「设置 → 技术 → 自动化 → 已计划的动作」中,将 模型 设为 library.loan,方法 设为 cron_send_due_reminder(需在模型上实现 @api.model 方法)。
1.3.3 截图占位

1.3.4 本节练习
- 选择题:以下哪类最适合「用户点菜单填表 → 生成正式业务单据」?(A)
Model(B)TransientModel(C)AbstractModel - 简答题:说明
mail.thread与mail.activity.mixin各解决什么用户可见问题。 - 实操题:给某业务模型加上
mail.thread,并在表单视图加入message_ids的 chatter 区(可照抄标准 sale/order 视图结构)。
参考答案提示:1. B。2. thread 侧重消息流与跟踪;activity 侧重待办与截止日期。3. 参考 mail 模块 form 中的 div class="oe_chatter"。
1.4 底层机制
1.4.1 知识要点
- ir.actions.*:动作在数据库中注册;菜单、按钮、绑定动作都通过 action id 或 xml id 引用。
- 注册表(Registry):启动时加载模型、视图、访问 rights;二次开发以 声明式 XML/Python 为主。
- 旧资料中的
fields_view_get等内部入口,在新版中由 视图引擎与 ORM 替代;迁移以 官方 ORM Changelog 为准。
1.4.2 案例
案例 A:按钮打开已定义动作
<button name="%(my_module.action_book_list)d" type="action" string="全部图书"/>
案例 B:Python 中触发动作
action = self.env.ref('my_module.action_book_list').read()[0]
return action
1.4.3 截图占位

1.4.4 本节练习
- 简答题:
ir.actions.act_window中res_model的作用是什么? - 实操题:在 XML 中定义
ir.actions.act_window与ir.ui.menu,使用ref将菜单绑定到该动作。
参考答案提示:1. 指定打开的业务模型。2. 见第九章动作与第十章菜单案例。
1.5 Odoo 19 架构概览(新增)
1.5.1 知识要点
- Python 版本:以官方 19.0 安装文档为准;升级项目时同步 Docker/CI 镜像 与依赖。
- PEP 420 命名空间:包布局更规范;自定义模块包名 勿与标准库、
odoo子包冲突。 - WebClient:浏览器端 OWL 应用;与后端通过 JSON-RPC 等 交互。
- Assets:静态资源按 bundle 组织,支持懒加载与缓存指纹。
1.5.2 案例
案例:在 manifest 声明一段后端专用脚本(示例)
# __manifest__.py 片段
'assets': {
'web.assets_backend': [
'my_module/static/src/js/**/*.js',
'my_module/static/src/scss/**/*.scss',
],
},
1.5.3 截图占位

1.5.4 本节练习
- 简答题:为何生产环境不建议长期开启
--dev=all? - 实操题:列出你当前环境
python -V与 Odoo 文档要求的版本是否一致,并记录一项需升级的依赖。
参考答案提示:1. 暴露调试信息、资产未合并影响性能与安全。2. 以本机输出为准。
1.6 模块清单(Manifest)详解(新增)
1.6.1 知识要点
__manifest__.py 常用键:
| 键 | 作用 |
|---|---|
name / version |
显示名与版本号 |
depends |
依赖列表,决定加载顺序 |
data / demo |
安装/演示数据文件 |
assets |
前端资源 |
installable |
是否出现在应用列表 |
application |
是否作为应用根展示 |
license |
许可证 |
依赖:禁止循环依赖;最小依赖 便于复用。生命周期:安装 → -u 升级 → 卸载;升级会重载视图与执行迁移。
1.6.2 案例
案例 A:最小可安装 manifest
{
'name': 'My Library',
'version': '19.0.1.0.0',
'depends': ['base'],
'data': ['security/ir.model.access.csv', 'views/book_views.xml'],
'installable': True,
'license': 'LGPL-3',
}
案例 B:错误依赖的后果
声明 depends: ['sale'] 却仅用 base 模型 → 模块过重、安装慢;未声明 mail 却 _inherit mail.thread → 安装报错或运行期缺表。
1.6.3 截图占位

1.6.4 本节练习
- 判断题:
demo列表中的文件在生产库升级时一定会被执行。( ) - 填空:控制模块是否在应用列表出现的键是 __。
- 实操题:为你自己的模块补充
application: True与category,在应用列表中观察分类变化。
参考答案提示:1. 错(仅安装 demo 时或显式加载策略下,依版本与配置而定,生产勿依赖 demo)。2. installable(或同时 application 控制是否根应用)。3. 以界面为准。
本章综合练习
- 画一张示意图:用户点击菜单 → ir.ui.menu → ir.actions.act_window → ir.ui.view → models.Model,标注数据方向。
- 列举三项:仅改视图
invisible无法解决的安全问题。 - 拓展:阅读官方 ORM Changelog,摘录一条与「环境
env」相关的变更到学习笔记。
本章对应白皮书目录:第一章 概念总论。