开 源 智 造 咨 询 有 限 公 司 ( o s c...

276
开源智造咨询有限公司(OSCG- 企业快速开发平台 Odoo 开发手册 参考:Document Reference 版本 1.0 除非盖章,否则打印后为非控制文件 开源智造咨询有限公司( OSCG 企业快速开发平台 ODOO 开发手册 版本 日期 修改描述 撰写人 审查人 批准1.0 2018-01-20 初版 肖相扶 Checker Approv er OS Consulting Group 参考: Document Reference

Upload: others

Post on 21-May-2020

35 views

Category:

Documents


0 download

TRANSCRIPT

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

开 源 智 造 咨 询 有 限 公 司 ( O S C G )

企 业 快 速 开 发 平 台 O D O O 开 发 手 册

版本 日期 修改描述 撰写人 审查人 批准人

1.0 2018-01-20 初版 肖相扶 Checker Approv

er

OS Consulting Group 参考: Document Reference

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

参考列表

参考 描述

缩写词列表

缩写词 说明

OSCG OS Consulting Group

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

目录

1. ODOO:企业信息化快速开发平台 ................................................................................. 12

1.1 ODOO 概述 .................................................................................................................... 12

1.1.1 Odoo 是什么 .......................................................................................................... 12

1.1.2 Fabien Pinckaers 小传 .......................................................................................... 15

1.1.3 Odoo 值多少钱 ...................................................................................................... 18

1.1.4 Odoo 企业应用简介 .............................................................................................. 20

1.2 ODOO 技术架构 ............................................................................................................ 25

1.2.1 MVC 三层结构 ...................................................................................................... 25

1.2.2 系统架构 ............................................................................................................... 26

1.2.3 模块 Module .......................................................................................................... 27

1.3 POSTGRESQL 概述........................................................................................................ 28

1.3.1 PostgreSQL 历史 ................................................................................................... 28

1.3.2 PostgreSQL 特性 ................................................................................................... 30

1.3.3 PostgreSQL 用户 ................................................................................................... 31

1.4 ODOO 用户案例 ............................................................................................................ 34

1.4.1 Odoo 用户概要 ...................................................................................................... 34

1.4.2 法国邮政(La Poste)案例 ....................................................................................... 36

1.4.3 法国达能案例 ....................................................................................................... 37

1.4.4 丰田案例 ............................................................................................................... 39

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

1.5 ODOO 助中国企业创新 ................................................................................................ 47

1.5.1 企业信息化概要 ................................................................................................... 47

1.5.2 小型企业 ............................................................................................................... 50

1.5.2.1 小企业如何信息化 ........................................................................................ 50

1.5.2.2 小企业 Odoo 案例.......................................................................................... 52

1.5.3 中型企业 ............................................................................................................... 53

1.5.3.1 中型企业如何信息化 .................................................................................... 53

1.5.3.2 中型企业 Odoo 案例 ...................................................................................... 56

1.5.4 大型企业 ............................................................................................................... 58

1.5.5 Odoo 部署方式 ...................................................................................................... 60

2. ODOO 模块开发指南 ........................................................................................................ 62

2.1 启动/停止 ODOO 服务 ................................................................................................... 62

2.2 创建一个 ODOO 模块 .................................................................................................... 62

2.2.1 模块的组成............................................................................................................ 62

2.2.2 模块结构 ............................................................................................................... 63

2.2.3 对象关系映射 ....................................................................................................... 69

2.2.4 模型字段 ............................................................................................................... 69

2.2.4.1 通用属性 ........................................................................................................ 70

2.2.4.2 简单字段 ........................................................................................................ 70

2.2.4.3 预设字段 ........................................................................................................ 71

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

2.2.4.4 特殊字段 ........................................................................................................ 71

2.2.5 data 文件 ............................................................................................................... 72

2.2.6 操作和菜单 ........................................................................................................... 74

2.3 基本视图 ....................................................................................................................... 77

2.3.1 通用视图声明 ....................................................................................................... 77

2.3.2 Tree 视图 ............................................................................................................... 78

2.3.3 Form 视图 ............................................................................................................. 78

2.3.4 Search 视图 ........................................................................................................... 82

2.4 模型间的关系 ............................................................................................................... 83

2.4.1 关系字段 ............................................................................................................... 86

2.5 继承 ............................................................................................................................... 91

2.5.1 模型继承 ............................................................................................................... 91

2.5.2 视图继承 ............................................................................................................... 93

2.6 计算字段和默认值 ..................................................................................................... 100

2.6.1 Dependencies 依赖关系 ...................................................................................... 101

2.6.2 默认值 ................................................................................................................. 104

2.7 ONCHANGE .................................................................................................................. 105

2.8 模型约束 ..................................................................................................................... 108

2.9 进阶视图 ..................................................................................................................... 111

2.9.1 Tree 视图 ............................................................................................................. 111

2.9.2 Calendars 视图 .................................................................................................... 113

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

2.9.3 Search 视图 ......................................................................................................... 117

2.9.4 Gantt .................................................................................................................... 119

2.9.5 Graph views 图视图 ............................................................................................ 122

2.9.6 Kanban 视图 ........................................................................................................ 125

2.10 SECURITY .................................................................................................................. 129

2.10.1 基于 group 的访问权限机制 ............................................................................ 129

2.10.2 访问权限 ........................................................................................................... 129

2.10.3 记录规则 ........................................................................................................... 132

2.11 WIZARD 向导 ............................................................................................................. 134

2.11.1 启动 Wizard ....................................................................................................... 135

2.12 国际化 ....................................................................................................................... 138

2.13 报表 ........................................................................................................................... 141

2.13.1 打印的报表 ....................................................................................................... 141

2.13.2 仪表盘 ............................................................................................................... 144

2.14 WEBSERVICES ........................................................................................................... 146

2.14.1 XML-RPC Library ............................................................................................. 147

2.14.2 JSON-RPC Library ............................................................................................ 148

3. ODOO ORM API 接口开发手册 ................................................................................... 151

3.1 RECORDSETS ............................................................................................................... 151

3.1.1 访问字段 ............................................................................................................. 152

3.1.2 记录缓存和预读机制 ......................................................................................... 153

3.1.3 Recordset 操作 ..................................................................................................... 154

3.1.4 其他 Recordset 的操作 ........................................................................................ 155

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

3.2 ENVIRONMENT ............................................................................................................ 157

3.2.1 Environment 变更 ................................................................................................ 158

3.3 通用的 ORM 方法 ...................................................................................................... 159

3.4 创建模型 ..................................................................................................................... 161

3.4.1 计算型字段 ......................................................................................................... 162

3.4.2 关联字段 ............................................................................................................. 164

3.4.3 低级的 SQL ......................................................................................................... 165

3.5 旧 API 与新 API 的兼容性 ........................................................................................ 166

3.6 模型的引用 ................................................................................................................. 168

3.6.1 模型通用属性 ..................................................................................................... 168

3.6.2 增删改查 ............................................................................................................. 170

3.6.3 RecordSet 运算 .................................................................................................... 179

3.6.4 环境转换 ............................................................................................................. 180

3.6.5 检索字段、视图 ................................................................................................. 182

3.6.6 其他方法 ............................................................................................................. 183

3.6.7 预设字段 ............................................................................................................. 185

3.6.8 常见字段 ............................................................................................................. 185

3.7 方法修饰符 ................................................................................................................. 186

3.8 字段 ............................................................................................................................. 193

3.8.1 基础字段 ............................................................................................................. 193

3.8.1.1 计算型字段 .................................................................................................. 195

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

3.8.1.2 关联字段 ...................................................................................................... 197

3.8.1.3 公司依赖型字段 .......................................................................................... 197

3.8.1.4 增量定义 ...................................................................................................... 197

3.8.2 关联字段 ............................................................................................................. 201

3.9 继承与扩展 ................................................................................................................. 205

3.9.1 继承类 ................................................................................................................. 206

3.9.2 扩展类 ................................................................................................................. 207

3.9.3 代理类 ................................................................................................................. 208

3.10 搜索域 ....................................................................................................................... 210

3.11 从旧 API 转换到新 API ........................................................................................... 212

3.11.1 对旧 API 方法的自动桥接 ............................................................................... 215

4. ODOO 视图 VIEW 开发手册 ......................................................................................... 217

4.1 通用结构 ..................................................................................................................... 217

4.2 继承视图 ..................................................................................................................... 218

4.2.1 获取视图 ............................................................................................................. 218

4.2.2 视图请求 ............................................................................................................. 218

4.2.3 继承视图 ............................................................................................................. 218

4.3 列表视图 ..................................................................................................................... 220

4.4 表单视图 ..................................................................................................................... 224

4.4.1 结构组件 ............................................................................................................. 224

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

4.4.2 语法部分 ............................................................................................................. 225

4.4.3 业务视图指南 ..................................................................................................... 227

4.4.3.1 hearder 状态栏 ............................................................................................ 228

4.4.3.2 sheet .............................................................................................................. 230

4.4.4 系统配置 ............................................................................................................. 236

4.4.5 对话框须知 ......................................................................................................... 236

4.4.6 wizard 向导须知 .................................................................................................. 237

4.5 透视图表 GRAPH ........................................................................................................ 237

4.5.1 Pivot ..................................................................................................................... 238

4.6 看板视图 KANBAN ..................................................................................................... 239

4.6.1 按钮和字段.......................................................................................................... 241

4.7 日历视图 CALENDAR .................................................................................................. 241

4.8 甘特视图 GANTT ........................................................................................................ 244

4.9 图形视图 DIAGRAM .................................................................................................... 245

4.10 搜索视图 SEARCH .................................................................................................... 247

4.10.1 Search 默认条件 ............................................................................................... 250

4.11 打印视图 QWEB ...................................................................................................... 250

5.THEME TUTORIAL 主题教程 ...................................................................................... 252

5.1WEB 设计工具介绍 ..................................................................................................... 252

5.1.1 从普通的内容管理系统(CMS)到 odoo ......................................................... 253

5.1.2 odoo 默认的主题结构 ......................................................................................... 253

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

5.2 思考 "模块化" ............................................................................................................. 254

5.2.1 Odoo 的 xml 文件和概述 ................................................................................... 257

5.2.2 更改主题 ............................................................................................................. 257

5.3 创建一个主题模块 ......................................................................................................... 1

5.3.1 编辑 __manifest__.py ............................................................................................. 1

5.3.2 安装你的主题 ......................................................................................................... 2

5.4 一个 ODOO 页面的结构 .................................................................................................. 2

5.4.1 扩展默认 Header ..................................................................................................... 3

5.5 创建一个特定的页面布局 ............................................................................................. 4

5.6 添加样式 ......................................................................................................................... 7

5.7 创建代码片段 ................................................................................................................. 8

5.8 代码片段的选项 ........................................................................................................... 10

5.8.1 选项组属性 ........................................................................................................... 10

5.8.2 默认的选项方法 ................................................................................................... 11

5.8.3 Javascript 选项 ...................................................................................................... 13

5.9 编辑参考指南 ............................................................................................................... 15

5.9.1 layout 布局 ............................................................................................................ 15

5.9.2 Media 媒体 ............................................................................................................ 15

5.10 搜索引擎优化(SEO)最好实践 ............................................................................. 16

5.10.1 促进内容插入 ..................................................................................................... 16

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

5.10.2 页面分割 ............................................................................................................. 17

5.10.3 定义一个标题和一个描述 .................................................................................. 18

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

1. ODOO:企业信息化快速开发平台

1.1 ODOO 概述

1.1.1 Odoo 是什么

Odoo,以前叫 OpenERP,是欧洲开发的一个企

业应用软件套件,包括一个企业应用快速开发平台,以

及几千个各方开发的企业应用模块,Odoo 适用于各种

规模的企业应用。

Odoo 功能模块涵盖了各方面的企业应用:CRM、

订单处理(销售订单和采购订单)、 电子商务、

MRP、财务、库存、门店零售、项目管理等等。

Odoo 是完全的模块化结构,初始安装时候,系统

没有加载任何应用模块,而后可以根据需要安装对应的

功能模块。

Odoo 常见的模块列表:

– 财务 Accounting

– 客户关系管理 Customer Relationship Management (CRM)

– 仓库管理 Warehouse Management

– 制造资源计划 Manufacturing Resource Planning (MRP)

– 采购管理 Purchase Management

– 项目/服务管理 Project/Service Management

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

– 人力资源管理 Human Resource Management (HRM)

– 内容管理 Content Management (CMS)

– 文档管理 Document Management

– 车辆管理 Fleet Management

– 门店零售 Point of Sale (POS)

– 应用模块在线构建器 Application Builder

自适应手机 App 端:

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

⚫ Odoo 为所有用户提供了快速使用方案,Odoo 可以快速定制和修改模块功

能和操作界面;

⚫ Odoo 的编写语言是 Python,JavaScript,XML;

⚫ Odoo 是跨平台的,可以运行于 Linux, Unix-Like, OS X 以及 Windows。

Odoo 版本发布历史。截至目前,Odoo 最新版是 2017 年 10 月份发布的 11.0 版,历

年的版本发布时间如下:

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

1.1.2 Fabien Pinckaers 小传

Odoo 的创始人是比利时的怪才 Fabien Pinckaers。

Fabien 13 岁时就对商业和技术产生了浓厚的兴趣,他开发

并销售了他的第一份商用管理软件:"Les Taxix Verts"。

在法国Louvain-la-Neuve大学学习计算机期间,Fabien

完成了很多项目,其中包括 Auction-in-Europe, Openstuff,

TinyERP (后来名为 OpenERP,后来又改为 Odoo)。

Auction-in-Europe 仅仅用了两年就成为比利时艺术市

场的领导者,每月销售 15000 份艺术作品,高于同期 eBay

的艺术作品销量。

Openstuff 很快成为当时欧洲 Linux 商店的领导者,Openstuff 网站销售 T 恤、海报等

商品。Fabien 运营这个网站到 2007 年。

Fabien 最大的成就是始于 2002 年 3 月的 TinyERP,TinyERP 后来成长为 OpenERP。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

OpenERP 发展到 8.0,超越 ERP 软件,增加了很多企业互联网功能后,又更名为 Odoo。

2002 年,Fabien 成立了自己的 TinyERP 公司。

年少轻狂的 Fabien 创办 TinyERP 时候,树立了一个梦想,他要用开源软件改变商业

世界。Fabien 立志用 3 年时间淘汰那个 770 亿美元市值的巨无霸公司 SAP。为了梦想成

真,Fabien 每周工作 7 天,每天工作 13 个小时。

但是现实与梦想有着遥远的距离,三年过去了,2005 年,SAP 没打败,Fabien 却被

达能的总裁们质问:“为什么我们要付几百万来买你这个小(Tiny)软件?”怪才 Fabien

第一次感觉,面对商业世界,自己是多么渺小。那一年,Fabien 将 TinyERP 更名为

OpenERP。

然而,梦想依旧,Fabien 仍然没有任何节假日地工作,朋友疏远了,女朋友跑了。

Fabien 带着公司开发了数以百计的 OpenERP 模块,开源社区开始成长起来。2010 年,

Fabien 31岁时候,公司终于发展为一家在三大洲拥有 100 多名员工的高技术企业。Fabien

终于不再需要为月末员工的工资发愁了(Fabien 曾为此挣扎努力了 4 年)。

Fabien 这个 100 多名员工的公司叫卖着 OpenERP 服务,解决了温饱问题。但是,

Fabien 意识到,直接服务于终端客户分散了打造精品的资源和精力,OpenERP 这个产品

强大却又漏洞百出!

Fabien 要转变商业模式!Fabien 希望将服务型公司转变为软件发布公司,投入更多的

精力和资源于产品研发。Fabien 改变了商业模式:不再为客户直接提供服务,转而建设一

个强大的合作伙伴体系和提供维护服务支持。

这个模式需要很多的钱,2010 年 Fabien 设法筹募了 300 万欧元的资金。投资人包括

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

欧洲最大的风险资本 Sofinnova Partners,法国唯一在 10 年中达到 10 亿欧元市值的 lliad

公司的老板 Xavier。后来比利时政府也给了无偿资金资助。又在 2014 年的时候,得到

XAnge (法国),SRIW (比利时), Sofinnova (法国) 三家机构联合投资 1000 万美元。

转变商业模式,有了资金的资助,Fabien 聚焦于产品研发,OpenERP 的功能迅速增

加和改善。2014 年,OpenERP 发布 8.0 的时候,产品已经不再局限于企业内部管理,而

是增加了很多互联网功能,向着打通企业内部、外部,上游、下游的全网级产品方向突进。

那一年,Fabien 意识到,仅仅做一个 ERP 产品是不可能打败比 OpenERP 大了 30 岁的对

手 SAP,因而转型向企业互联网应用方向突破,OpenERP 又改名为 Odoo。

为什么叫 Odoo,据说 Fabien 调查了一下,伟大的互联网公司的名字中都有很多 O,

如 Yahoo,Google,Facebook,因此取了三个 O 的名字 Odoo,希望这个名字能够带领

Odoo 成为一个新的伟大互联网公司!

随后,Odoo 每年发布一个新版本,2015 年发布 9.0,2016 年发布 10.0,2017 年发

布 11.0 。现在 Odoo 的合作伙伴遍布 100 多个国家,拥有 300 多万使用者。Fabien 的目

标是:提供易用的、经济的、企业全套商务解决方案,帮助企业集中精力发展业务,而不

需要在管理上浪费太多精力!

2009 年 6 月的 Trend Tendances 杂志将 Fabien 称为 "比利时的新比尔·盖茨"。Fabien

创建的 Odoo 获得了很多商业的、技术的以及创新领域的奖项,包括:

"Insead Innogator Price of the Year 2011"

"Trends Gazelle BW 2013"

"Deloitte Fast 50 2012"

"Bossie Award 2012"

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

1.1.3 Odoo 值多少钱

如果自己企业搭建一个类似 Odoo 那样的企业应用平台,并基于这个平台开发各种应

用系统,大概要花多少钱呢?

软件投入的评估,最简单的评估方法是 COCOMO 模型,COCOMO 基于软件的代码

行数估算投入成本。

Odoo 最早在 2005 年以开源软件的形式发布。2005 年到现在,发展了十几年,经过

了 10 多个版本的迭代,几千名开发人员共享了程序代码。除了 Odoo 官方发布的版本,还

有很多其他人和组织开发了一万多个功能模块。这些组织中最著名的是 Odoo 社区联盟

(Odoo Community Association,OCA)。

Odoo 的代码约 73 万行,大约相当于 197 人年(一个软件技术人员一年的工作量),

1970 万美元的投入。

OCA 的代码约 130 万行,大约相当于 281 人年,1684 万(OCA 年薪低一点)美元投

入。

下面是基于网站 www.openhub.net 的详细评估数据。网站 www.openhub.net 专门评

估各种开源软件的代码行数、活跃程度、软件价值。下图是来自该网站的 Odoo 代码行数

统计。

总的代码行数是 733,676,代码行数最多的是JavaScript,271,352行,其次是XML,

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

254,326 行,第三位的是 Python,168,060 行。

基于 COCOMO 模型的简单估算,Odoo 官方发布的版本,73 万行代码相当于 197 人

年(一个软件技术人员一年的工作量)的投入。按欧洲 IT 技术人员年薪 10 万美元估算,

大约相当于 1973 万美元的研发 投入。

以 OCA 为代表的第三方开发的功能模块代码行数如下:

总的代码行数大约 130 万行,相当于 281 人年(一个软件技术人员一年的工作量)的

投入。OCA 开发的代码技术含量低一些,按年薪 6 万美元估算,大约相当于 1684 万美元

的研发 投入。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

1.1.4 Odoo 企业应用简介

Odoo 开发了很多企业应用模块,涵盖企业网站、销售、财务、运营、生产制造、HR、

市场营销等方面应用。

⚫ 网站生成器:可视化的企业网页编辑器,提供了很多网页模板,可以拖拉

制作企业网站;

⚫ 电子商务:在企业网站上开设网上商店,典型应用场景是,让经销商在企

业网站上下单订货;

⚫ 博文:企业网站上搭建新闻报道频道,用于发布企业新闻

⚫ 论坛:在企业网站上搭建论坛,用于企业内部或企业合作伙伴发帖提问或

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

讨论

⚫ 幻灯片:在企业网站上发布宣传文档(PDF)、宣传海报(PNG 等图档格

式)、视频等各种资料。资料文档可以设定企业内部、合作伙伴、公众公开等不同权

限。

⚫ 在线客服:企业网站上增加在线客服功能,即浏览网站的人可以留言,后

台客服人员可以在线回复。

⚫ Appointments:会议预约,管理客户拜访或网络会议的时间及参与人员,

系统会自动邮件提醒相关人员安排参会。

⚫ CRM 客户关系:客户关系管理模块,管理业务员,销售线索、客户商机、

销售漏斗;

⚫ POS:门店零售收银管理

⚫ 销售:销售订单管理,包括订单审核、订单发货、订单开票

⚫ 订阅:服务订购管理,例如按年或按月交费的维护服务管理,管理内容包

括:哪个客户,什么时候到期,购买了什么服务。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

⚫ 会计:财务总账管理,包括会计账簿凭证、会计凭证、会计报表

⚫ 发票:销售开票、采购收票,应收账款、应付账款管理

⚫ 开支:费用报销管理,包括在线提交报销单、报销审批、报销付款、报销

做账

⚫ 库存:仓库管理,包括多仓库多库位规划,入库、出库、调拨、盘点等仓

库操作,库存查询,物流追踪等功能;

⚫ 工时单:Timesheet,类似于员工日报。技术服务类企业,通过工时单核算

服务成本。

⚫ 项目:项目管理,包括项目、任务、子任务跟踪管理

⚫ 采购:采购订单管理,包括订单审核,采购入库,采购开票

⚫ 帮助台:服务工单管理,包括工单分配、工单状态、工单服务评价管理。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

⚫ MRP:物料清单 BoM,缺料计算,MRP 运算,生产订单管理

⚫ PLM:产品生命周期管理,主要包括工程变更单 ECO 管理,BoM 版本管

理,

⚫ 设备:生产设备维修保养管理

⚫ 质量:产品质量管理,可以在入库、出库等物流操作节点定义质检点,质

检点上,系统会强制要求质检人员确认。

⚫ 招聘:人员岗位、人员招聘流程管理

⚫ 员工:员工档案、劳动合同管理

⚫ 车队:公司车辆租借、油卡管理

⚫ 休假:员工年假、请休假管理

⚫ 评价:员工绩效考核管理

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

⚫ Marketing Automation:自动化市场营销,例如,可以设定自动化规则,

订单额累计满 100 万的经销商升级为银牌,或者设定规则,自动筛选本月生日的顾客,

发送生日促销邮件。

⚫ 电邮营销:设定邮件模板,群发邮件

⚫ 活动:市场活动管理,包括活动申请、审核、活动在线报名、活动样品管

理。

⚫ 调研:市场在线调查,可以自定义调查表单,在线提交调查表

⚫ 讨论:注册用户之间即时聊天,例如,围绕报价单的在线讨论,讨论过程

系统会自动关联到该报价单

⚫ 电子签呈:在线手写签名的小功能

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

1.2 ODOO 技术架构

1.2.1 MVC 三层结构

Odoo 基于 Model-View-Controller (MVC) 架构构建。MVC 架构的目的是,分离底层数

据库数据,中间层业务规则,和上层的信息展现。例如,如果修改了底层数据表结构,不

希望影响数据展现方式。今天的企业应用系统,数据展现有多种方式,例如 PC 端,手机

端,对内展示,对外展示,同样的数据,不同的设备不同的对象,展示形式不同。MVC 这

种松耦合有助于保持展现层的灵活性。

Model 层告知 Controller 有数据变化,Controller 更新 Views 层的数据展示。View 层告

知 Controller 用户执行了什么操作,Controller 更新 Model 层数据或者从 Model 层获取用户

需要的数据给 View 层。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

1.2.2 系统架构

Odoo 系统主要由三个部分组成:

PostgreSQL 数据库服务器:数据库层,Odoo 的所有业务数据,以及大部分的配置数

据(视图格式、菜单显示、权限设置等)都保存在数据库中。

Odoo 应用服务器:应用服务器包括三部分,底层负责和 PostgreSQL 接口的 ORM 层,

中间的包含企业业务规则的业务逻辑层,上层的负责和客户端的浏览器之间通讯的网络层。

不包括第三方开发的模块,Odoo 应用服务器由 25 万多行的 XML 代码和 18 万多行的

Python 代码构成。

Web 客户端:Web 客户端由 27 万多行 Javascript 和 3 万多行 CSS 及 HTML 代码构

成,运行于用户电脑的浏览器中。企业版的 Web 客户端是手机自适应的,同时也带有安卓

和 IOS 的 App 客户端程序。Web 客户端向服务器发出请求,获取数据并以不同的方式显示

结果(例如客户信息列表,数据表单,图表、日历等)。用户修改操作时候,Web 层发送

修改数据到应用服务器,更新到数据库。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

1.2.3 模块 Module

对任何企业应用而言,Odoo 的价值在于各种应用模块,Odoo 的各种模块实现企业的

各种需求。Odoo 的模块运行于 Odoo 应用服务器,也只需要 Odoo 应用服务器,不再需要

其他软件组件(Odoo 应用服务器自带 Web 服务器,ORM 等组件)。

Odoo 模块通常由 Python 代码编写的 model 业务对象,XML 编写的初始化数据文件,

XML 或 QWeb 编写的视图 View 文件,用户多步交互的 Wizards,QWeb 及 Python 编写的

Report 文件,Javascript 及 CSS 编写的界面组件等部分组成。

一个 Odoo 模块看上去就是一个文件夹,文件夹中包含若干元素:

⚫ 业务对象:写在 Python 类中,Odoo 将基于相应的配置自动持久化

⚫ 数据文件:XML 或 CSV 文件处理元数据(视图或报告)、配置数据(***modules

parameterization)、演示数据等。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

⚫ Web 控制器:处理来自 Web 浏览器的请求

⚫ 静态 Web 数据:Web 界面或网站用到的图像、CSS 文件或 JavaScript 文件

1.3 POSTGRESQL 概述

PostgreSQL 是 Odoo 支持的数据库。PostgreSQL 是起源于大学的一个历史很长的开

源数据库系统。包括美国航天局 NASA、德国证券交易中心、中国的平安、腾讯的微信支

付、阿里巴巴的阿里云都在用 PostgreSQL 数据库。

1.3.1 PostgreSQL 历史

上世纪 70 年代 Edgar F. Codd 博士研究关系型数据库理论时候,实际开发了一个数据

库产品 Ingres。Ingres 名字是“Interactive Graphics Retrieval System”的缩写。后来的

Sybase、微软的 SQL Server 也是基于这个 Ingres 数据库开发的产品。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

1985 年,加利福尼亚大学伯克利分校的 Michael Stonebraker 博士等人继承数据库

Ingres,发布了 Postgres 数据库,这就是 PostgreSQL 的起源。1989 年,Postgres 1.0 在

研究机构小范围内发布。1994 年,Postgres 发布了 4.2 版,随后,Postgres 作为大学数据

库研究项目终止了。

当时的 Postgres 数据库的查询语言不是 SQL,是 Postgres 独有的。当时参与人员的

感觉是“Postgres 的引擎非常好,但没有方向盘,就像是用操作杆驾驶的汽车一样非常难

操作”,也就是说 Postgres的查询语言太难用了。因此,伯克利分校的研究生们用 SQL替

换了 Postgres 的查询语言,发布了 Postgres95 。

虽然替换成 SQL 了,Postgres95(相当于 Postgres 版本 5.0)在性能和可靠性方便还

有待改善,另外开发人员太少也是个问题。因此,1996 年,参考 FreeBSD 的做法,

Postgres 开发者分成了 Core 和 Committer 两种角色推进。

1995 年,脱离大学研究项目,发布了 Postgres95,而后,Postgres 社区发起了命名

的讨论,是用 Postgres95,Postgres96 呢,还是别的名字?最后决定,因为是在 Postgres

的基础上增加了 SQL,因而用 PostgreSQL 的名字。版本也改成了 5.0,6.0 的版本号。

1996 年,改名为 PostgreSQL 后一个月,1997 年 1 月,发布了 PostgreSQL 6.0。6.5

版则增加了 MVCC(MultiVersion Concurrency Control),性能大幅提高,同时增加了日

文、中文等多字节字符的支持。

2000 年前后出现了几个 PostgreSQL 服务的企业。最初诞生的是“PostgreSQL Inc”,

该公司虽然在 1997 年设立了社区网站,但商业上却没有成功,慢慢地消失了。2000 年

“Great Bridge Inc”公司诞生了,该公司目标是做“数据库领域的红帽子”,同年入选了

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

“25 Coolest Global Companies”,但也没多久就消失了。2004 年诞生了 EnterpriseDB

公司,该公司一直持续到现在。该公司依托 PostgreSQL 开发者一起,面向企业提供

PostgreSQL 技术支持服务。

PostgreSQL 的 分 支 很 多 , 基 于 PostgreSQL 的 衍 生 品 也 很 多 。 SRA OSS 的

“PowerGres”,“富士通的 Enterprise PostgreSQL”广为人知。其他的如数据仓库产品

“Netezza”(被 IBM 收购),并行处理产品“Greenplum”,亚马逊 Web Service 之一

的数据库服务“Redshift”,集群数据库“StormDB”,流数据库“TelegraphCQ”及其

商业版“Truviso”(被思科收购),国内的阿里云 PostgreSQL 数据库服务等,都是基于

PostgreSQL 开发的。

1.3.2 PostgreSQL 特性

PostgreSQL 是一个功能强大的开源数据库系统。经过长达 20 年以上的积极开发和不

断改进,PostgreSQL 已在可靠性、稳定性、数据一致性等获得了业内极高的声誉。目前

PostgreSQL 可以运行在所有主流操作系统上,包括 Linux、Unix(AIX、BSD、HP-UX、

SGI IRIX、Mac OS X、Solaris 和 Tru64)和 Windows。PostgreSQL 是完全的事务安全性

数据库,完整地支持外键、联合、视图、触发器和存储过程(并支持多种语言开发存储过

程)。它支持了大多数的 SQL:2008 标准的数据类型,包括整型、数值值、布尔型、字节

型、字符型、日期型、时间间隔型和时间型,它也支持存储二进制的大对像,包括图片、

声音和视频。PostgreSQL对很多高级开发语言有原生的编程接口,如C/C++、Java、.Net、

Perl、Python、Ruby、Tcl 和 ODBC 以及其他语言等,也包含各种文档。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

作为一种企业级数据库,PostgreSQL 以它所具有的各种高级功能而自豪,像多版本

并发控制(MVCC)、按时间点恢复(PITR)、表空间、异步复制、嵌套事务、在线热备、复

杂查询的规划和优化以及为容错而进行的预写日志等。它支持国际字符集、多字节编码并

支持使用当地语言进行排序、大小写处理和格式化等操作。它也在所能管理的大数据量和

所允许的大用户量并发访问时间具有完全的高伸缩性。目前已有很多 PostgreSQL 的系统

在实际生产环境下管理着超过 4TB 的数据。一些 PostgreSQL 系统的极限值如下表所列:

极限值:

最大单个数据库大小不限

最大数据单表大小 32 TB

单条记录最大 1.6 TB

单字段最大允许 1 GB

单表允许最大记录数不限

单表最大字段数 250 - 1600 (取决于字段类型)

单表最大索引数不限

由于 PostgreSQL 的优异性能,它已赢得最终用户和业内的多次大奖,包括 Linux 新媒

体(Linux New Media)的最佳数据库奖和 5 次 Linux 期刊编辑选出的最佳数据库奖。

1.3.3 PostgreSQL 用户

PostgreSQL 的知名用户包括 Skype、NTT、Salesforce 的 Heroku 云数据库平台、

Etsy 等大型企业。PostgreSQL 在日本数据库市场有超过 60%的市场占有率,大量的制造

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

业、游戏行业、企业 ERP 系统都采用了 PostgreSQL。

2010 年以来,随着中国互联网的迅速崛起, PostgreSQL 在国内迅速发展,受到越来

越多的企业尤其是互联网企业重视。据公开报导,腾讯、阿里、中国平安、苏宁、去哪儿

网、斯凯网络等企业都在大规模应用 PostgreSQL。尤其是腾讯,基于 PostgreSQL 架构了

TDW(Tencent Distributed Warehouse 腾讯分布式数据仓库)平台,存储腾讯海量核心业务

数据。

TDW(Tencent Distributed Warehouse):腾讯分布式数据仓库,是腾讯公司海量数据

处理平台的核心部件,承载着腾讯公司各业务群产品数据(如互联网增值、SNS、网游、

电商等)的储存和处理工作。目前,TDW 支持百 PB 级数据的离线存储和计算,为业务提

供海量、高效、稳定的大数据平台支持和决策支持。

据 2017 年 PostgreSQL 技术大会腾讯大数据工程师的发言,腾讯 TDW 数据仓库集群

超过 100 台服务器,超过 100T(1T = 1000G)的数据量。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

苏宁 Citus 系统案例。苏宁 Citus 是一个大数据分析系统,业务场景大致是,每 5 分钟

从多个业务系统抽取业务数据到 Citus 数据库,每次要更新 Citus 中 10 张明细表,大约更

新30万条数据记录。Citus中保留最近若干天的数据,数据总量大约3000万条记录。Citus

数据库每 5 分钟分析抽取一次数据,形成 30 多张不同用途的业务报表。

Citus 原来是基于 IBM 的 DB2 数据库构建的,如下图所示,DB2 已经不堪重负,尤其

是大促时候。而且,预计一年后数据量将增长到 10 倍,也就是每 5 分钟要更新 300 万条数

据,Citus 数据量将达到 3 亿条数据记录。

苏宁 Citus 技术团队调查研究之后,决定用 PostgreSQL 替换 IBM DB2 数据库。替换

后的压力测试表明,系统实际性能表现超过设计目标 50%以上。上线运行半年以来,效果

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

相当稳定。苏宁的案例表明,PostgreSQL 性能表现远优于 IBM 的 DB2!

1.4 ODOO 用户案例

1.4.1 Odoo 用户概要

关于 Odoo 全球的用户,我们来看一些数据:

⚫ Odoo 目前全球有 300 万使用者

⚫ Odoo 系统上每天新创建的数据库超过 1000 个

⚫ Odoo 和 Word、Excel、PowerPoint 一样,是法国学士学位的必修课程

⚫ Odoo 在世界各地有 800 多个官方认证的合作伙伴,其中中国有 20 多个

⚫ Odoo 的翻译语言包覆盖 100 多个国家和地区

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

⚫ Odoo 开源社区有 1800 多名活跃的技术开发人员

⚫ Odoo 的应用商店有一万多个 App 模块,还在以每个月 300 多个的速度新增

Odoo 全球知名的用户,包括法国邮政,法国达能,欧洲丰田,中国联想(欧洲),

中兴通讯(德国)等大公司。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

1.4.2 法国邮政(La Poste)案例

La Poste 是法国当地最大的邮政业务服务商,有 30 万名员工,1.7 万个营业网点。业

务包括邮政储蓄,邮件收发,快递,零售卖场。La Poste 也是欧洲第二大的邮政公司。

La Poste 的四大业务板块

• 邮件收发: Le Courrier

• 货运快递:Le Colis-Express

• 邮政储蓄:La Banque Postale

• 零售卖场:La Poste Retail Outlets

【业务需求】

替换集团内部员工帮助台(helpdesk),包括从微软的 MS Access 数据迁移到 Odoo

系统

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

【解决方案】

基于 Odoo 标准的客户服务工单(CRM Helpdesk)模块定制一个内部员工帮助台系统。

Odoo 标准的服务工单模块包括工单处理升级机制、工作流管理工具等。

定制化模块:

1) 扩展 Odoo 标准对象表单,满足法国邮政众多实体和员工的组织结构;

2) 优化视图改善用户界面,优化数据库索引提高响应速度;

3) 扩展标准的 CRM 模块,定制专用的视图和工单升级处理机制;

4) 定制 Access 到 Odoo 的复杂数据升级工具

1.4.3 法国达能案例

达能的案例首先发表在 2011 年 9 月份的法国“商业技术”杂志,作者是达能集团的 IT

主管。原文是法文的,本文从原文的英文版摘编中翻译而来。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

SAP 作为大型集团通用和完整的后端业务处理平台是非常合适的,

但对于大集团众多的分支机构,他们需要快速实施,灵活扩展,更易使

用的系统。为了解决集团大型 ERP 和集团分支机构需求的矛盾,达能集团选择 Odoo 作为

众多分支机构的业务操作平台。

达能是传统的 SAP 用户,过去 10 年,达能在 SAP 上投入了大量预算。但事实证明,

SAP 不适合小型分支机构的要

求。SAP 系统,即使非常小的配

置 修 改 都 是 一 件 非 常 复 杂 的 事

情。达能选择 Odoo 作为 SAP 系

统的补充方案的优点是:Odoo 是

一个轻量级的技术平台,其次可

以 充 分 利 用 开 源 社 区 的 技 术 资

源。

达能首先在哥伦比亚分支机构推行 Odoo 应用,哥伦比亚采取了开发和实施并行推进

的敏捷方法推进,效果非常好。这个敏捷方法得以实施的基础是,Odoo 软件修改非常快

速,可以做到每周发布新版本;其次,用在线看板做开发管理,看板列出用户需求和完成

进度,用户和开发人员组成的项目小组可以在项目 Wiki 上快速查看项目需求、开发、完成

情况等信息。

哥伦比亚的经验证明在一个相对较小的范围,Odoo 实施是非常快速和成功的。哥伦

比亚的成功,让达能大胆地在澳大利亚推行包括生产制造模块的大范围实施。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

达能的 IT 团队对 Odoo 的实施过程进行了一些反馈分析。他们指出基于 Odoo 的敏捷

方法的灵活性,对用户接受度有非常积极的影响。事实上,实施过程中,用户需求得到即

时反馈和即时培训。

现在达能在定义一些准则,评估哪些地方什么时候可以更多地替换为 Odoo 系统。他

们考虑在所有小型运营实体推进 Odoo,再逐步集成到 SAP。

1.4.4 丰田案例

[ 作者 ] 查理恩 · 路易斯

丰田(法国)物流车辆工贸公司,用了短短 6 个月时间,便成功地导入、实施了 Odoo

开源 ERP 系统。之所以能在如此短的时间内

创造这一 ERP 项目实施奇迹,主要得益于丰

田(法国)公司,与 Odoo 的官方合作伙伴

— 斯 麦 里 开 源 系 统 管 理 咨 询 有 限 公 司

(Smile Open Source Solutions)之间,开展了卓有成效地合作。为深入探究 Odoo ERP

系统对管理提升和效率改善所起的促进作用,我们从比利时出发,奔赴丰田(法国)物流

车辆工贸公司,现场采访了信息部经理,克莱尔·康泰特(Cyril Cottet)先生。

问:康泰特先生,请首先简要介绍一下贵公司好吗?

答: 好的。

我们丰田(法国) 物流车辆工贸公司,是丰田众多产供销一体化公司中的一员,隶属于

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

丰田物流车辆集团,而该集团的母公司,是位列世界 500 强的丰田汽车集团。丰田(法国)

公司的主营业务分为部分:一部分是在欧洲市场内,制造和销售各种叉车;另一块业务,

也是我们最为擅长的,就是制造和销售带有”动平衡调节芯片”的柴、汽油卡车。同时,

我们也在日本、中国、美国的工厂,采购一些卡车,然后将这些卡车运到欧洲,进行销售。

在有些情况下,我们也会依据客户的一些特殊需要,在我们的法国工厂里,为客人预定的

卡车进行改装,满足客户的定制化要求。

问:在咨询公司成功导入 Odoo之前,贵公司面临的最大挑战有哪些,Odoo解决方案是

如何满足贵公司特殊需求的?

答:你说的一点不错,在引入 Odoo 之前,我们确实度过了一段地狱般的岁月。但现在,

由于

我们拥有了 Odoo, 那些让人发疯的难题都已经得到了解决!(大笑)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

近几年,丰田物流车辆集团,对内部进行了几次较大的组织架构调整。为的是对现有

的销售流程进行再造,以便使遍布全欧洲的分销商,能够更好、更快地向欧洲的 3 个产销

中心顺畅下单、并获得良好的物流发货服务。目前,丰田物流车辆公司在欧洲有 3 个产销

中心,1 个就设在这儿的法国公司,另外 2 个分别设在瑞典和意大利。

面对日渐火爆的“卡车大规模个性化定制”这一市场发展趋势,我们专门自行开发了

一款服务这一新型业务的软件系统,分别在欧洲的 3 个中心使用。因为在市场上,客户定

制卡车的销量,已占到总销量的 40%。但很快麻烦也就来了,问题出在我们意大利的工厂。

整个意大利工厂,在表面业务红红火火的背后,出现了严重的问题,就是这家工厂各环节

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

的管理流程与这个系统无法协同,普遍存在“两张皮”现象,例如: 无法顺畅地下单,下

了单也无法将信息知会到关联部门;已下的订单,不是工厂没有安排生产,就是采购部没

有在国外采购,甚至在国外采购好的卡车,也不能在合同的交货期内组织发运、交货、开

票、收款。

虽然问题多多,但时至今日,我们自行开发的这款“卡车大规模个性化定制软件系

统”,仍然在使用。面对这一现状,我们曾考虑可否能换掉这个系统,也就是说,探讨

“选用一个新系统、来全部撤下旧系统”的可能性。

于是,我们开始在软件市场上进行了寻找、了解、比较,一番“做功课”之后,我们

逐渐搞明白了,我们面临的问题是“二选一”,就是:要么是公司自行开发一个新系统,

来代替自行开发的老系统;要么就从市场上购买一套新系统,来彻底取代老系统。

我们以前没用过 ERP, 也就是说,

我们对 ERP 系统,相当缺乏了解,这需

要我们对这一未知领域,要进行了解和

探索。由于欧洲物流车辆市场的竞争几

近白热化,我们不能进行风险太大的投

资。我们只能考虑在短期就能获得回报

的投资。经过对 ERP 供应商接触后,我

们发现,市场上已有的几个较大 ERP 品

牌公司的已有产品,无法满足我们的要求。在这种情况下,我们倾向于追求一个尽可能经

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

济实惠的解决方案:要么自己开发一套系统、要么购买一套成本较低的 ERP 系统。幸运的

是,我们最终在市场上发现了 Odoo 开源 ERP 系统,它们的产品、价格、服务,与我们的需

求高度吻合!

了解到OdooERP后,影响我们做出决策的最后一个因素,是ERP实施所化时间的长短。

面对公司原有软件不能满足公司发展的紧迫需要,我们欧洲集团的管理层已达成共识,务

必要在 2015 年 4 月-6 月间,成功导入新的 ERP 系统。当我们回过头来,重新评估前面谈到

的两个方案(自己开发、或全部购买)时,我们仍无法确定哪个方案会更好。因为,根据

当时已接触到的 ERP 供应商、咨询公司的有关信息,我们分析,即使在 5 月底勉强完成系

统,仍会存在大量的风险和问题。到最后,一家名为斯麦里的管理咨询有限公司,从众多

咨询公司中脱颖而出;这家公司,作为 Odoo 的官方合作伙伴和经销商,向我们清晰展示了

ERP 方案,针对我们业务现状,给出了切合实际的整体解决方案,并表示,如果能在 2014

年 9 月双方签约,那么在 2015 年 3 月底前,由斯麦里(Smile)管理咨询有限公司负责实

施,完全有把握将成熟的系统部署到我们公司的“数据中心”上。

这样,经过多方比较和分析,我们最终签约、选定“斯麦里(Smile)管理咨询有限公

司”,作为我们实施 Odoo ERP 的咨询公司。

在 2014 年 9 月到次年 3 月间的 6 个月里,我们和咨询公司一起,做了大量的系统集

成、部署等工作,各项工作快速推进。

Cyril Cottet 丰田(法国)公司

IT 经理

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

问:那您能介绍一下最终交付的解决方案吗?

答:哦,是这样的!最后上线的系统,远远超出了销售模块的范围。当然喽,我们现

在的订单已全在 Odoo ERP 销售模块里下了。销售订单下达后,我们接下来要关心的,就是

要确保已下单的车辆,能够按期采购到,这样就触发了库存管理模块(WMS)。

之后,进销存模块,便启动了货品的采购流程。采购模块内有 2 路流程被触发,根据

事先设定的订单分发的逻辑安排,一种可能是,我们采购的车辆,会在欧洲本土进行制造,

这样的话,进销存模块会把订单导入“Odoo 制造”模块,由它来驱动工厂制造、装配;另

一种情况是,有些车辆,需要向日本、美国、中国的工厂进行跨国采购,这样的话,订单

就自动被导入“Odoo 采购”模块了。

后来,我们还追加购买了“Odoo 财务”模块,因为我们需要在 Odoo 系统里,直接开

出发票,以便向客户结算货款。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

为了将原有那款软件充分利用起来,我们自行开发,将原有软件集成到 Odoo ERP 系统

中。要知道,我们以前虽然

自行开发了不少程序,但在

这之前,我们对 Odoo 却一无

所 知 。 这 是 一 个 能 激 发 潜

能、而且引人入胜的工作。

至今天,我们已经拥有了一

套集成的、可自动运行的系

统解决方案:自从导入 Odoo ERP 系统后,从下单到发货、开票收款的整个流程中,公司的

财务系统、装运发货系统、工厂制造系统(法国的、或是日本的)已全部实现协同,这真

正实现了业务、财务的一体化,企业管理水平和效益得到了极大提升。完成这么复杂的

ERP 系统导入、集成工作,只花费了短短几个月,咨询公司的工作效率,实在是太高了!

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

问:那请问,Odoo 开源 ERP 系统如何促进了贵公司业务的开展呢?

答:这套 OdooERP 系统,是在今年 5 月 18 日正式上线的,而现在刚到 8 月份,所以用

精准的数据来评估这套系统对业务改善的贡献,还为时尚早。

但毫无疑问的是,这必将大大节省我们 IT 系统的运维费用;因为我们知道,以前我们

使用的是“闭门造车”自行开发的系统,而现在我们拥有的 OdooERP,是 Odoo 公司官方发

布、并获得业界广泛赞誉的管理信息系统。此外,我们相信 Odoo 公司,在将来新发布的版

本中,会集成更多、更好的功能模块,而且集成开发本身也会变得越来越简便、容易,这

将为我们的用户创造更多的价值,而这正是我们孜孜以求的!

促使我们选择 OdooERP 系统的一个关键因素,是 Odoo 系统实施所需的时间较短。试想,

如果没有在市场上发现 Odoo,而选择了其他品牌的 ERP 系统,我真的不能保证能在集团要

求那么短的时间内,能够确保系统成功上线。

选定 Odoo 的咨询公司“思麦里”后,该公司不仅帮助我从忙于救火、晕头转向的状态

中解放出来,而且帮助我的日常管理工作,向着越来越顺、越来越有秩序的方向发展,这

使我非常有信心,在 3 月底前完成 ERP 系统的导入、实施工作。对我来说,这是送给我的

最好礼物! 只用了短短数月时间,就将如此庞大、复杂的整体解决方案,顺利实现上线,

真实奇迹!但我们真的做到了,而且没有出现任何问题!(大笑)

问:最后,请您描述一下贵公司最看重 Odoo 开源 ERP 系统的哪些优势呢?

Odoo ERP 系统运行到今天,使我们更加深刻认识到,Odoo 系统带给我们的最大优势,

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

就是 Odoo ERP 系统的开源性,这使它变成了一个可扩展性极强的平台。基于这个优势,今

后,我们公司可以随着业务的不断发展,来不断扩展我们的信息系统。将来,我们有可能

用 Odoo 的现有模块,来彻底替换我们那些老旧的自行开发的模块。到那时,我将彻底告别

那些让我头疼不已的老款软件,你知道,我是多么期待那一天早日到来呀!(大笑)

如果选择的 ERP 系统,能进一步挖掘我们已有资产的潜能,那对我们来说,实在是太

好了!在我们 IT部门,最好的资产就是人材!在法国公司,我们拥有软件开发、IT技能最

好的工程师。眼下,我们尚不能用一个商业化的投融资模块、来替换原有的投融资模块,

因为这会导致基于挖掘原有模块中已存储的数据而产生的附件价值信息将损失殆尽。因此,

最好的办法,就是通过培训我们的工程师,让他们在 Odoo ERP 开源平台上,不断开发公司

业务需要的新功能模块,来不断提升 ERP 系统的价值。在已有的 ERP 多个品牌中,只有

Odoo ERP 系统,拥有这个得天独厚的开源优势,这对我最重要,也是我最看重的!

最后,作为个人用户,我认为,Odoo ERP 系统,他的交互界面都非常人性化,对这一

点,公司上下赞赏不已!,面向未来,随着公司的发展,将会有越来越多的功能模块集成

到 Odoo ERP 中,系统功能也将会大大扩展,到那时,公司将能够看到,这套 Odoo ERP 系

统给公司带来的价值,也会越来越多、越来越大!

1.5 ODOO 助中国企业创新

1.5.1 企业信息化概要

下图来自陈启申老师的著作《ERP 从内部集成起步》,这个图描绘了一个较为完善的

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

企业信息化全貌,包括了企业内部信息化和上下游集成信息化。

⚫ MRP/MRPII:制造资源计划,生产、仓库、采购、销售、财务等企业内部

信息化;

⚫ DRP:分销资源计划,销售预测、分销渠道、库存管理的信息化;

⚫ CRM:客户关系管理,终端消费者、经销商、代理商、维修服务商,和客

户相关的所有主体管理的信息化;

⚫ SCM:供应链管理,供应商、采购、物流、仓库、生产,打通内部和供应

商的产品供应全流程管理的信息化;

⚫ B2C 电商:直接销售到终端客户的网络下单商城或者手机下单的微商城;

⚫ B2B 经销商订货:针对经销商、代理商、批发商的网络订货商城;

⚫ B2B 网上采购:针对供应商的网络集中采购平台。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

企业不是一日发展壮大的,企业的业务模式是逐步成熟的,内部的部门建制是逐步完

善的,下游的经销网络或客户群体是逐步发展的,上游的供应网络是逐步开发的。相应地,

各个业务板块的信息化系统,随着业务发展,逐步建设而成。

企业在成长过程中,通常要经历几次大的阵痛转型,每一次阵痛过后,企业跃上一个

更高平台。企业信息化的建设过程,通常也需要几次阵痛整合,每一次整合过后,企业信

息化上升到一个更完善的平台,支撑企业未来若干年发展。经过若干次阵痛整合,最后形

成企业完善、高效的信息化系统,支撑企业战略实现。

不同发展阶段,企业有不同的特点和战略取向,相应的,企业信息化重点也不一样。

⚫ 小型企业:年销售额几百万到几千万的企业,外部业务不稳定,企业受外

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

部环境影响很大。内部部门建制不完善,管理不规范。这一阶段的企业,战略上重点

在于放权,自由作战。信息化以局部信息化和信息集中存储为目标,局部信息化支撑

企业自由发展,集中存储帮助企业累积信息资源。

⚫ 中型企业:年销售额几亿到十几亿,企业业务基本稳定,部门建制齐全。

战略上,这一阶段的企业重点在于从分散走向集中,从自由走向规范。这一阶段,信

息化重点在于整合前一阶段形成的局部信息化系统,追求规范化和集成化。规范化是

集成化的前提,内部集成化帮助企业提高部门协作效率,上下游集成化帮助企业在整

个供应链中占据优势地位。

⚫ 大型企业:年销售额几十亿以上,企业形成了多个业务板块,有一些成熟

稳定的业务,也有一些正在开发的业务。组织架构上,集团控制,各业务板块独立发

展。战略上,企业更多的是从财务层面集中控制,业务上放权自由发展。由于企业庞

大,业务板块多,各板块成熟程度不一,信息化上,集团财务系统实现集中控制,集

团信息部制定统一的技术要求和接口要求,各业务板块自由选择信息系统。

1.5.2 小型企业

1.5.2.1 小企业如何信息化

小型企业,年销售额几百万到几

千万,没有 IT 专业人才。外部环境,

小企业往往在市场夹缝中生存,受市

场波动影响很大,没有自己稳定成熟

的商业模式。内部环境,缺乏人才,

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

没有完善的部门建制,人员流动大。企业信息化建设以局部信息化为主,由各个业务主管

自主选择适合自己的信息系统,支撑各业务部门自由成长。

这一阶段的企业,信息化有两个目的,其一是,信息集中存储,持续累积信息资产。

例如销售部门实施客户关系管理系统(CRM),客户信息,销售信息都集中存储于 CRM

系统。业务员流失,业务员开发的客户,客户商机和成交历史不会流失,常年累月,形成

公司客户资产。

其二是,局部信息化,保持信息系统灵活性。外部环境的变化,企业业务模式调整,

信息系统不会成为羁绊。如果信息系统大而全,业务模块过于一体化,集成化,信息系统

反而不能适应业务变化。

Odoo 有一万多个业务模块,可以适应企业各种业务需求。小型企业局部信息化的时

候,各个业务部门可以选用 Odoo 的不同模块,例如 CRM 模块,库存和采购模块,财务模

块,生产模块,HR 模块,电商模块等。信息化初期,为了降低信息化难度,甚至可以架

构多套 Odoo 系统,每个部门一套 Odoo 专用系统。各部门独立,上线容易,见效快。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

局部信息化系统都架构于 Odoo 平台,好处是,统一的操作界面,统一的数据平台。

系统推广期间减少基础数据维护,降低人员培训成本。其次,Odoo 是一个开源软件,无

论架构多少套,无论使用多少功能模块,无论多少人使用,软件本身都是免费的。第三,

公司发展壮大了,需要从分散走向集中,从自由走向规范的时候,统一的平台,系统整合

难度小,整合成本低。

1.5.2.2 小企业 Odoo 案例

LEMAN EASTERN 是 法

国公司 LEMAN Group 的中国

分公司,LEMAN Group 专业

制造五金工具,主要产品为切

割 / 研 磨 砂 轮 等 。 LEMAN

EASTERN 的业务模式是,客

户下单购买 Leman 的产品,LEMAN EASTERN 一方面仓库备货主营产品,一方面向中国

其他供应商购买辅材,一并交付客户。从销售订单(SO)到采购订单(PO),主营商品

是典型的备货销售模式(MTS, Make to Stock),辅材是典型的 MTO(Make to Order)

模式。

LEMAN EASTERN 开办第一年就实施了 Odoo 系统。利用 Odoo 系统,仅一名销售后

勤,服务了 6 名销售业务员,其中还有两名法国业务员。系统上线两年以来,管理着 300

多个客户,100 多个供应商,处理了 800 多个销售订单,3000 多个采购订单。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

佛山澳卡斯生产家电、电子产品等小电器的小型环形变压器。工厂产品有 100 多款,

仓库常备物料有 3000 多种。澳卡斯

花了几个月时间,在没有请技术公司

协助的情况下,自己实施了 Odoo 的

采购和库存模块。大概一年以后,请

了技术公司培训和支持,升级 Odoo

系统,实施了采购、库存、生产和财务模块。

澳卡斯的 Odoo 系统上线使用 5 年以来,支持了设计主管、物料员、生产主管、采购

主管、仓管员、财务主管等核心岗位工作。系统中管理着 8000 个物料,1000 多个成品和

半成品 BoM 表。累计处理了 4000 多个生产订单,10000 多个采购订单。5 年以来累积的

业务数据,系统数据库 2.5 G 左右。

1.5.3 中型企业

1.5.3.1 中型企业如何信息化

中型企业年销售额从几亿到十几亿。经过初期的艰苦创业,小企业逐渐找到自己的核

心市场定位,规模越做越大,人员和部门越来越多,业务模式越来越清晰稳定。在这个过

程中,企业往往已经形成了若干个局部信息化系统,企业建立了 IT 部门,有了专职 IT 人

员。

中型企业形成了自己的市场定位,基本解决了外部市场问题。但由于前期的自由快速

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

发展,各业务部门各自为政,部门间协调成本越来越高,内耗越来越严重,跑冒滴漏开始

出现。中型企业需要集成化整合,包括内部整合以提高效率,上下游整合以提高产业链的

话语权。

中型企业需要一个统一的信息化平台,解决前期局部信息化系统形成的信息孤岛问题。

需要一个集成化系统,帮助企业统一指挥,形成高效低内耗的市场竞争优势。

陈启申老师在著作《ERP 从内部集成起步》中,非常强调企业内部集成。企业内部集

成,主要指信息流、资金流、物流的集成。市场信息、客户需求信息,这是信息流的来源,

这个信息从企业前端,经过分解变换,一直流到企业后端。信息流从前端流向后端,形成

企业内部的指挥系统,伴随指令的执行,原料进来,生产加工,成品流向市场,形成从企

业后端到前端的物流。物流流向市场,市场买单,资金流从市场流入,根据各部门物流中

形成的成本预算,资金流再补充到各部门,促进再生产能力,形成良性循环。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

信息流推动物流,物流推动资金流,三流合一,企业才能高效运转。如何推动三流合

一呢,核心是需要一套集成化的信息平台。市场信息进入信息化平台,信息化平台分解信

息到各个部门,各部门按指令完成工作。各部门工作中产生的物流信息和成本信息进入信

息化平台。随着物流的推进,业务部门启动资金回款工作,资金进入信息化平台,根据成

本预算信息,信息化平台将自己分配到各业务部门。

信息流、物流、资金流,三流合一。具体到细节,每个企业都有很多行业化、个性化

的需求,每个企业都不一样。Odoo 作为中型企业信息化核心平台,有几个好处,其一是,

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

Odoo 有很多 CRM、库存、生产、财务等一万多个标准化模块,企业信息化的很多共通基

础已经具备,不需要再开发。其二,Odoo 是一个世界范围被验证过的,成熟的、快速的

企业信息化开发平台,基于这个平台可以高效开发企业的个性化需求。其三,Odoo 系统

的伸缩性极强,可以单机部署快速满足小企业需求,也可以集群部署适应大用户大数据需

求。其四,开源软件,可以应企业业务变化按需修改,没有 License 限制。

1.5.3.2 中型企业 Odoo 案例

深圳五谷磨房食品集团,是一家行业领先的天然营养食

品公司。五谷磨房在全国 300 多个城市设立了近 4000 家品

牌直营专柜。五谷磨房在湖北建有 14 万平米的无菌加工配送基地,在广西建有淮山生产加

工基地。五谷磨房快速成长,曾获评中国最具投资价值企业 50 强、最具成长潜力企业 20

强、创新成长企业 50 强、中国成长企业百强等诸多荣誉。

五谷磨房选用了 Odoo 作为信息化基干平台。在实施 Odoo 之前,五谷磨房有大小十

多个系统,数据打架,错误频出,数据无法整合。实施 Odoo 之后,废掉了其他系统,都

整合到了 Odoo 系统。包括 4000 多个专柜的配送及零售管理系统,工厂生产管理系统,总

部营销物资采购及配送系统,财务核算系统等。4000 个超市专柜,开发了手机 App,营业

员通过手机 App 输入销售订单到 Odoo 后端系统。Odoo 后端系统根据销量信息安排配货,

根据销量预测安排生产。生产部根据排产计划安排生产和采购。市场部根据促销计划安排

市场活动,采购活动物资。财务部每个月从系统拉销售毛利表,计算各业务部门绩效。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

五谷磨房 Odoo 系统运行三年多,目前数据库已经超过 50G,最大的一个表,记录数

量超过 2 亿条。

西域作为国内专业 MRO 自营电商,通过 EDI 对接整合上游厂家实时库存数据,通过

Punch-out 接口对接客户 ERP,实现客户非生产物料的电商化采购,提升供应链货期, 确

保正品行货、降低采购成本、保障服务质量。通过在线系统对接金融机构,实现了应收款

在线保理,目前有多家大型基金已是西域股东。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

西域是一家非常创新的工

业品电商公司。在产业链中,

它需要对接上下游系统。在内

部,它需要管理订单销售到采

购的复杂供应流程,条码化高

效仓库物流,工业品多方式的

开票及回款管理,不同板块业

务员绩效管理,等等。

西域创新的业务模式,高速的业务发展,西域信息化平台特点是,客户多,供应商多,

产品多,账期多,接口多。Odoo 开源开放的特点,非常适合西域打通上下游产业;Odoo

极强的伸缩性,非常适合西域业务高速增长;Odoo 丰富的进销存功能模块,有助于西域

快速构建内部管理平台。

西域 2016 年开始实施 Odoo 系统,经过一年多的开发实施,实现了内部的订单处理、

条码化的上下架仓库管理、开票及应收账款管理、存货成本核算、绩效考核,外部的大客

户 ERP 订单接口对接、银行支付网关对接、金税三期税票接口对接、金融机构供应链信用

信息对接。

1.5.4 大型企业

大型企业规模大,业务多,IT 系统异常复杂。下图是联想高级副总裁王晓岩在联想

“技术大会”上发布的联想的 IT 架构图。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

大型企业的骨干 IT 系统一般基于久经验证的大型企业软件架构,如联想,后端骨干业

务系统基于 SAP ECC 架构,前端骨干系统基于 SAP CRM 架构。围绕骨干系统,有很多

小系统,以及自己研发的系统,如联想 IT 架构图上的蓝色部分。以 LOIS 为例,LOIS 是一

个连接 PLM(产品生命周期管理)系统和前端 CRM 系统的小系统。LOIS 从 PLM 抽取数

据,形成销售需要的产品数据抛给 CRM。

出于成本考虑,以及灵活性、扩展性等考虑,大型企业集团本部用 SAP 等大型软件架

构,区域公司的信息系统更多地会选择一些其他系统架构。如前面法国达能的案例,达能

选择 Odoo 架构各个分支机构的管理系统,通过和 SAP 的接口,集成到集团信息系统。实

际上,联想欧洲事业部的销售计划系统也是基于 Odoo 架构的。还有中兴通讯德国公司的

集中采购管理系统也是基于 Odoo 开发的。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

Odoo 是一个全网级企业信息系统快速开发平台,可以做后端业务系统,如 MRP、仓

储采购等,也可以做前端业务系统,如 CRM、电商网站等。大企业的复杂 IT 架构中,分

支机构的 ERP 系统、前端电商系统、上游的供应商系统、内部的多系统间连接器,这些都

可以考虑基于 Odoo 架构。

Odoo 作为开源软件,没有 License 限制,大企业内部可以架构多套 Odoo 系统。统一

的系统,技术一致,接口打通,数据容易共享。应用操作一致,内部推行容易,人员适应

成本低。

1.5.5 Odoo 部署方式

Odoo 个人学习和快速入门,最方便的部署方法是,直接上 Odoo 官网下载 Windows

版的 All in One 安装包,一路点击“下一步”就可以完成安装。默认情况下,浏览器打开

本机地址 8069 端口即可方法 Odoo。

小企业生产用,最简单的部署方法是,买一台阿里云或腾讯云,选用 Ubuntu 操作系

统。Odoo 官网下载 Ubuntu 安装包,直接安装。阿里云 4 核 CPU,8G 内存的服务器,可

以支持 30 个用户流畅使用。

中型企业应用,用户数多,数据量大。初期可以考虑 8 核 CPU,16G 内存的 Linux 服

务器两台,一台跑 Odoo,一台跑 PostgreSQL 数据库服务器。这个配置可以支持至少 300

个用户流畅使用。以后,随着信息化平台应用的深入,功能模块增多,数据量增大,用户

越来越多,可以部署多台服务器集群系统。Odoo 和 PostgreSQL 都支持集群部署方式。

下图来自 Haibin Zhou 发表在领英上的“Odoo 集群/负载均衡”部署方案。前端通过

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

Nginx 实现负载均衡,中间应用层部署多台 Odoo 服务器。Session 会话连接等信息保存于

Odoo 间共享的 Redis 数据库。PG Pool 实现 PostgreSQL 集群部署。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

2. ODOO 模块开发指南

2.1 启动/停止 ODOO 服务

Odoo使用 CS架构,其中客户端通过 web浏览器使用 RPC的方式访问 Odoo服务器。

通常,业务逻辑和扩展交给服务器端实现,尽管 odoo 同样支持在客户端添加新特性

(例如新的交互表现形式)。

只需在 shell 中调用 odoo-bin 命令就可以启动 odoo 服务,如有必要可以添加文件的完

整路径:

odoo-bin

可以通过连按两下 Ctrl-C来或者直接结束对应的 OS进程来停止服务。

2.2 创建一个 ODOO 模块

可以把扩展内容按‘模块’的组织形式进行打包,并在数据库中选择性地加载它们,

用以对服务器和客户端进行升级。

Odoo 模块可以向 Odoo 系统添加新的业务逻辑,或者更改现有的业务逻辑。Odoo 涉及

的业务范围非常广泛,上一个模块刚刚把国家的会计规则添加到 Odoo 的通用会计支持中,

而下一个模块就可以为公共汽车机群的实时可视化添加了服务。

Odoo 通过一个又一个模块提供相应服务。

2.2.1 模块的组成

一个 Odoo 模块包含若干个元素:

业务对象:

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

写在 Python 类中,Odoo 将基于相应的配置自动持久化

数据文件:

XML 或 CSV 文件处理元数据(视图或报告)、配置数据(***modules parameterization)、

演示数据等。

Web 控制器:

处理来自 Web 浏览器的请求

静态 Web 数据:

Web 界面或网站用到的图像、CSS 文件或 JavaScript 文件

2.2.2 模块结构

每个模块需要放在模块目录中的一个目录中。模块目录是使用-addons-path 选项指定

的。

其他大多数命令行选项也可以使用配置文件来设置。

Odoo 模块(的各项参数)由它的 manifest 决定。参见清单文件。

Odoo 模块还包括一个__init__.py 文件,通过它来导入模块中的 python 文件。

例如,如果模块中包含了一个独立的 mymodule.py 文件,那么__init__.py 文件可能需

要这么写:

from . import mymodule

Odoo 提供了一种机制来帮助建立一个新的模块,Odoo -bin 有一个次级指令 scaffold

(脚手架)来创建一个空模块:

$ odoo-bin scaffold <module name> <where to put it>

该命令为模块创建子目录,并自动为模块创建一堆标准文件,大多数只包含注释代码

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

或 XML 文件。这些文件的用法将在本教程中逐一解释。

练习

创建模块

使用上述命令行创建一个空模块 Open Academy,并在 Odoo 中安装这个模块。

1. 输入命令 odoo-bin scaffold openacademy addons.

2. 把 manifest 文件放到你的模块

3. 不要考虑其他文件

openacademy/__manifest__.py

# -*- coding: utf-8 -*-

{

'name': "Open Academy",

'summary': """Manage trainings""",

'description': """

Open Academy module for managing trainings:

- training courses

- training sessions

- attendees registration

""",

'author': "My Company",

'website': "http://www.yourcompany.com",

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

# Category 用于在列表中筛选模块

'category': 'Test',

'version': '0.1',

# 填写所有当前模块依赖的其他模块名称

'depends': ['base'],

# 安装模块时必然加载

'data': [

# 'security/ir.model.access.csv',

'templates.xml',

],

# 只在 demonstration 模式下加载

'demo': [

'demo.xml',

],

}

openacademy/__init__.py

# -*- coding: utf-8 -*-

from . import controllers

from . import models

openacademy/controllers.py

# -*- coding: utf-8 -*-

from odoo import http

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

# class Openacademy(http.Controller):

# @http.route('/openacademy/openacademy/', auth='public')

# def index(self, **kw):

# return "Hello, world"

# @http.route('/openacademy/openacademy/objects/',

auth='public')

# def list(self, **kw):

# return http.request.render('openacademy.listing', {

# 'root': '/openacademy/openacademy',

# 'objects':

http.request.env['openacademy.openacademy'].search([]),

# })

#

@http.route('/openacademy/openacademy/objects/<model("openacademy.

openacademy"):obj>/', auth='public')

# def object(self, obj, **kw):

# return http.request.render('openacademy.object', {

# 'object': obj

# })

openacademy/demo.xml

<odoo>

<!-- -->

<!-- <record id="object0"

model="openacademy.openacademy"> -->

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<!-- <field name="name">Object 0</field> -->

<!-- </record> -->

<!-- -->

<!-- <record id="object1"

model="openacademy.openacademy"> -->

<!-- <field name="name">Object 1</field> -->

<!-- </record> -->

<!-- -->

<!-- <record id="object2"

model="openacademy.openacademy"> -->

<!-- <field name="name">Object 2</field> -->

<!-- </record> -->

<!-- -->

<!-- <record id="object3"

model="openacademy.openacademy"> -->

<!-- <field name="name">Object 3</field> -->

<!-- </record> -->

<!-- -->

<!-- <record id="object4"

model="openacademy.openacademy"> -->

<!-- <field name="name">Object 4</field> -->

<!-- </record> -->

<!-- -->

</odoo>

openacademy/models.py

# -*- coding: utf-8 -*-

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

from odoo import models, fields, api

# class openacademy(models.Model):

# _name = 'openacademy.openacademy'

# name = fields.Char()

openacademy/security/ir.model.access.csv

id,name,model_id/id,group_id/id,perm_read,perm_write,perm_creat

e,perm_unlink

access_openacademy_openacademy,openacademy.openacademy,model_op

enacademy_openacademy,,1,0,0,0

openacademy/templates.xml

<odoo>

<!-- <template id="listing"> -->

<!-- <ul> -->

<!-- <li t-foreach="objects" t-as="object"> -->

<!-- <a t-attf-

href="{{ root }}/objects/{{ object.id }}"> -->

<!-- <t t-esc="object.display_name"/> -->

<!-- </a> -->

<!-- </li> -->

<!-- </ul> -->

<!-- </template> -->

<!-- <template id="object"> -->

<!-- <h1><t t-esc="object.display_name"/></h1> -->

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<!-- <dl> -->

<!-- <t t-foreach="object._fields" t-as="field"> -

->

<!-- <dt><t t-esc="field"/></dt> -->

<!-- <dd><t t-esc="object[field]"/></dd> -->

<!-- </t> -->

<!-- </dl> -->

<!-- </template> -->

</odoo>

2.2.3 对象关系映射

Odoo 的一个关键组件是 ORM 层。ORM 提供安全且易扩展的服务,并帮助开发人员

免于手工编写大部分的基础 SQL 语句。

业务对象是写在 Python 中继承 Model 的类的,Odoo 将会把这些类集成到自动持久化

的子系统中。

可以设置一些属性来配置模型。最重要的属性是_name,它是必需的,是 Odoo 系统

中定义模型的名称。以下是一个模型的最小完整定义:

from odoo import models

class MinimalModel(models.Model):

_name = 'test.model'

2.2.4 模型字段

从业务层面来说,字段决定了模型可以储存什么;不同的字段定义了不同的储存位置。

从代码层面来说,字段属于模型的一个参数。

from odoo import models, fields

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

class LessMinimalModel(models.Model):

_name = 'test.model2'

name = fields.Char()

2.2.4.1 通用属性

和模型类似,模型的字段可以进行参数化地配置属性:

name = field.Char(required=True)

一些属性是通用的,以下是最常见的一些:

string(unicode 类, 默认值:字段名称):

字段面向用户的 UI 标签

required(bool 类,默认值:False)

如果是 True,创建模型实例时该字段不能为空。它要么要有默认值,要么被手动/自

动赋值。

help(unicode 类, 默认值: ''):

文本且支持换行。为用户提供了一些该字段的解释说明。

index(bool 类,默认值:False)

要求 Odoo 在数据库创建该字段时为该列创建索引。

2.2.4.2 简单字段

字段可以分为两类:“简单”字段,它们的值可以被直接存储在数据库表中;“关

联”字段,用于关联(相同或不同模型)的记录。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

简单字段有 Boolean、Date、Char 等。

2.2.4.3 预设字段

Odoo 为所有模块创建了一些字段。这些字段由系统管理,不应该被手动更改。如有需

要,可以去读取它们的值。

id (Id)

记录在模型中的唯一标识。

create_date (Datetime)

创建记录的时间。

create_uid (Many2one)

创建记录的系统用户。

write_date (Datetime)

最后一次更改记录的时间。

write_uid (Many2one)

最后一次更改记录的系统用户。

2.2.4.4 特殊字段

默认情况下,Odoo 要求所有模型指定一个字段用于展示和筛选。可以通过重写

_rec_name 来自定义。

练习

定义一个模型

在 openacademy 模块中定义一个 Course 模型。每个 course 实例都有一个 title 和

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

description,所有 course 实例都必须又一个 title。

将 Course 类写到 openacademy/models/models.py 文本中。

openacademy/models.py

from odoo import models, fields, api

class Course(models.Model):

_name = 'openacademy.course'

name = fields.Char(string="Title", required=True)

description = fields.Text()

2.2.5 data 文件

Odoo 是一个高度数据驱动的系统。python 代码决定了模块的业务逻辑,被加载的数据

文件决定了模块的具体表现效果。

实际生产中,一些模块的存在只是为了向 Odoo 中添加数据

模块的数据通过数据文件、XML 文件中的<record>标签。

每个<record>标签都会创建或更新一条数据。

<odoo>

<record model="{model name}" id="{record identifier}">

<field name="{a field name}">{a value}</field>

</record>

</odoo>

model :这条记录对应的 Odoo 模型的名称。

id:这条记录的外部标识。Odoo 系统可以根据这个参数(而不是记录在数据库的 id)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

直接引用这条记录

<field>:有一个 name 参数,用来指定模型中的一个字段名称(比如 description)。它

的内容是这个地段是值。

数据文件必须在 manifest 文件中声明后才能加载,它们可以被声明在'data'列表中(总

是加载)或'demo'列表中声明(仅在演示模式中加载)。

练习

定义演示数据

往 Courses 模型中创建一些演示数据。

将这些数据放在 openacademy/demo/demo.xml 文件里。

openacademy/demo.xml

<odoo>

<record model="openacademy.course" id="course0">

<field name="name">Course 0</field>

<field name="description">Course 0's description

Can have multiple lines

</field>

</record>

<record model="openacademy.course" id="course1">

<field name="name">Course 1</field>

<!-- no description for this one -->

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

</record>

<record model="openacademy.course" id="course2">

<field name="name">Course 2</field>

<field name="description">Course 2's

description</field>

</record>

</odoo>

2.2.6 操作和菜单

界面上的 action(跳转)和菜单本质也是数据库中的记录,通常写在数据文件中。

action 可以通过以下三种方式触发:

1.点击菜单项(链接到指定 action)

2.点击视图中的按钮(如果它们和 action 有关的话)

3.作为一个对象链接上下文操作

因为菜单的数据结构有一定的复杂性,建议使用<menuitem>作为一种快捷方式来声明

一个 ir.ui.menu 实例,并将其绑定一个对应的 action 会更方便一些。

<record model="ir.actions.act_window" id="action_list_ideas">

<field name="name">Ideas</field>

<field name="res_model">idea.idea</field>

<field name="view_mode">tree,form</field>

</record>

<menuitem id="menu_ideas" parent="menu_root" name="Ideas"

sequence="10"

action="action_list_ideas"/>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

注意

Action 必须先于 menu 的连接使用定义。

数据文件将在载入时按顺序执行,action 的 id 必须已存在于数据库中才能使用。

练习

定义新的菜单项

定义新的菜单项供用户访问 OpenAcademy 菜单项下的课程。

用户应该能够:

使用列表形式显示所有课程。

创建/修改课程

在 openacademy/views/openacademy.xml 创建 action 和关联这个 action 的菜单。

把它添加到 openacademy/__manifest__.py 的 data 列表中。

openacademy/__manifest__.py

'data': [

# 'security/ir.model.access.csv',

'templates.xml',

'views/openacademy.xml',

],

# only loaded in demonstration mode

'demo': [

openacademy/views/openacademy.xml

<?xml version="1.0" encoding="UTF-8"?>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<odoo>

<!-- window action -->

<!--

The following tag is an action definition for a "window

action",

that is an action opening a view or a set of views

-->

<record model="ir.actions.act_window"

id="course_list_action">

<field name="name">Courses</field>

<field name="res_model">openacademy.course</field>

<field name="view_type">form</field>

<field name="view_mode">tree,form</field>

<field name="help" type="html">

<p class="oe_view_nocontent_create">Create the first

course

</p>

</field>

</record>

<!-- top level menu: no parent -->

<menuitem id="main_openacademy_menu" name="Open Academy"/>

<!-- A first level in the left side menu is needed

before using action= attribute -->

<menuitem id="openacademy_menu" name="Open Academy"

parent="main_openacademy_menu"/>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<!-- the following menuitem should appear *after*

its parent openacademy_menu and *after* its

action course_list_action -->

<menuitem id="courses_menu" name="Courses"

parent="openacademy_menu"

action="course_list_action"/>

<!-- Full id location:

action="openacademy.course_list_action"

It is not required when it is the same module -->

</odoo>

2.3 基本视图

视图定义了模型记录的呈现方式。每种类型的视图代表了一种可视化模式(列表形式、

聚合图像等)。可以根据视图的 type参数或者它的 id 调用视图。对于一般请求,指定 type

下 priority 最小的那个视图将被调用(因此每种类型的最低优先级视图是该类型的默认视

图)。

继承视图允许更改在其他地方声明的视图(例如:添加或删除内容)。

2.3.1 通用视图声明

视图是一个 ir.ui.view 模型的记录。视图类型是由 arch 字段的根元素决定的:

注意

The view's content is XML.

视图的内容部分参考 XML 规则,

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

因此,必须将名称是 arch 字段声明为 type="xml",以便正确解析。

<record model="ir.ui.view" id="view_id">

<field name="name">view.name</field>

<field name="model">object_name</field>

<field name="priority" eval="16"/>

<field name="arch" type="xml">

<!-- view content: <form>, <tree>, <graph>, ... -->

</field>

</record>

2.3.2 Tree 视图

Tree 视图,也叫列表视图,以列表形式显示数据记录。Tree 视图的根元素是 <tree>.

最简单的 Tree 视图,简单地列表显示所有字段,每个字段一列。

<tree string="Idea list">

<field name="name"/>

<field name="inventor_id"/>

</tree>

2.3.3 Form 视图

Form 视图通常用于创建和修改单个记录。

他们的根元素是<form>,由高阶结构元素( groups,notebooks)和交互元素

(buttons,fields)组成。

<form string="Idea form">

<group colspan="4">

<group colspan="2" col="2">

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<separator string="General stuff" colspan="2"/>

<field name="name"/>

<field name="inventor_id"/>

</group>

<group colspan="2" col="2">

<separator string="Dates" colspan="2"/>

<field name="active"/>

<field name="invent_date" readonly="1"/>

</group>

<notebook colspan="4">

<page string="Description">

<field name="description" nolabel="1"/>

</page>

</notebook>

<field name="state"/>

</group>

</form>

练习

使用 XML 自定义一个 form 视图

为 Course 实例创建自己的 form 视图,应该显示记录的 name 和 description。

openacademy/views/openacademy.xml

<?xml version="1.0" encoding="UTF-8"?>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<odoo>

<record model="ir.ui.view" id="course_form_view">

<field name="name">course.form</field>

<field name="model">openacademy.course</field>

<field name="arch" type="xml">

<form string="Course Form">

<sheet>

<group>

<field name="name"/>

<field name="description"/>

</group>

</sheet>

</form>

</field>

</record>

<!-- window action -->

<!--

The following tag is an action definition for a

"window action",

练习

Notebooks

在 Course 的 form 视图中,把 description 字段放在一个选项卡中,这样如果日后再

加一个选项卡就可以更有序地展示更多的相关字段。

如下修改 Course 的 Form 视图:

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

openacademy/views/openacademy.xml

<sheet>

<group>

<field name="name"/>

</group>

<notebook>

<page string="Description">

<field name="description"/>

</page>

<page string="About">

This is an example of notebooks

</page>

</notebook>

</sheet>

</form>

</field>

Form 视图也可以使用 html 来实现更灵活的布局。

<form string="Idea Form">

<header>

<button string="Confirm" type="object" name="action_confirm"

states="draft" class="oe_highlight" />

<button string="Mark as done" type="object"

name="action_done"

states="confirmed" class="oe_highlight"/>

<button string="Reset to draft" type="object"

name="action_draft"

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

states="confirmed,done" />

<field name="state" widget="statusbar"/>

</header>

<sheet>

<div class="oe_title">

<label for="name" class="oe_edit_only" string="Idea

Name" />

<h1><field name="name" /></h1>

</div>

<separator string="General" colspan="2" />

<group colspan="2" col="2">

<field name="description" placeholder="Idea

description..." />

</group>

</sheet>

</form>

2.3.4 Search 视图

可以在 serach 视图中自定义模型的搜索条件。它们的根元素是<search>,它们由决定

了哪些字段可以用来筛选的<field>组成:

<search>

<field name="name"/>

<field name="inventor_id"/>

</search>

如果模型中不存在 search 视图,Odoo 将自动一个只允许根据 name 筛选的 search 视

图。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

练习

搜索 Courses

允许根据 title 或者 description 搜索 course 实例

openacademy/views/openacademy.xml

</field>

</record>

<record model="ir.ui.view" id="course_search_view">

<field name="name">course.search</field>

<field name="model">openacademy.course</field>

<field name="arch" type="xml">

<search>

<field name="name"/>

<field name="description"/>

</search>

</field>

</record>

<!-- window action -->

<!--

The following tag is an action definition for a "window

action",

2.4 模型间的关系

一个模型的记录可能会关联另一个模型的记录。例如,销售订单与客户相关联;它也与

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

订单明细行相关联。

练习

创建一个 session 模型

对于 Open Academy 模块,我们需要一个 session 模型,用来对不同时间段、不同听众

的课程进行统一管理。

创建一个 session 模型,每个 session 都有 name, start_date, duration, 若干个

seats。为这个模型增加一个菜单和 action,是新模块在菜单项中可见。

为会话创建模型。会话有名称、开始日期、持续时间和多个位置。添加 action 和菜单

项来展示 session 列表。

在 openacademy/models/models.py 创建 session 类

在 openacademy/view/openacademy.xml 加入相关视图。

openacademy/models.py

name = fields.Char(string="Title", required=True)

description = fields.Text()

class Session(models.Model):

_name = 'openacademy.session'

name = fields.Char(required=True)

start_date = fields.Date()

duration = fields.Float(digits=(6, 2), help="Duration in days")

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

seats = fields.Integer(string="Number of seats")

openacademy/views/openacademy.xml

<!-- Full id location:

action="openacademy.course_list_action"

It is not required when it is the same module -->

<!-- session form view -->

<record model="ir.ui.view" id="session_form_view">

<field name="name">session.form</field>

<field name="model">openacademy.session</field>

<field name="arch" type="xml">

<form string="Session Form">

<sheet>

<group>

<field name="name"/>

<field name="start_date"/>

<field name="duration"/>

<field name="seats"/>

</group>

</sheet>

</form>

</field>

</record>

<record model="ir.actions.act_window"

id="session_list_action">

<field name="name">Sessions</field>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<field name="res_model">openacademy.session</field>

<field name="view_type">form</field>

<field name="view_mode">tree,form</field>

</record>

<menuitem id="session_menu" name="Sessions"

parent="openacademy_menu"

action="session_list_action"/>

</odoo>

2.4.1 关系字段

关系字段用于链接两个相同货不同模型的记录。

关系字段有:

Many2one(other_model, ondelete='set null')

一个简单地链接其他记录的例子:

print foo.other_id.name

One2many(other_model, related_field)

一个虚拟的关系字段(不存在数据库中),Many2one 字段的逆反字段。One2many 字

段是一个若干个记录(可能为空)组成的集合:

for other in foo.other_ids:

print other.name

注意

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

因为 One2many 是一个虚拟的关系字段,所以对应的模块中必须有 Many2one 字段,且字

段名称必须与 related_field 字段一致。

Many2many(other_model)

双向多对多关系,任何模型的记录都将与另一个模型任意数量的记录相对应。

Many2many 字段是一个若干个记录(可能为空)组成的集合:

for other in foo.other_ids:

print other.name

练习

Many2one 关联

使用 Many2one 字段,将 Course 模型与 Session 模型关联起来。

每个 Course 有一个的 responsible_user;这个字段应该关联 Odoo 的内置模型 res.users

每个 Session 有一个 instructor; 这个字段应该关联 Odoo 的内置模型 res.partner

每个 Session 关联一个 course;这字段应该关联 openacademy.course 模型,且是必填的。

调整视图

1. 在模型中增加 Many2one 字段

2. 将它们添加到视图中。

openacademy/models.py

name = fields.Char(string="Title", required=True)

description = fields.Text()

responsible_id = fields.Many2one('res.users',

ondelete='set null', string="Responsible", index=True)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

class Session(models.Model):

_name = 'openacademy.session'

start_date = fields.Date()

duration = fields.Float(digits=(6, 2), help="Duration in days")

seats = fields.Integer(string="Number of seats")

instructor_id = fields.Many2one('res.partner', string="Instructor")

course_id = fields.Many2one('openacademy.course',

ondelete='cascade', string="Course", required=True)

openacademy/views/openacademy.xml

<sheet>

<group>

<field name="name"/>

<field name="responsible_id"/>

</group>

<notebook>

<page string="Description">

</field>

</record>

<!-- override the automatically generated list view for courses -->

<record model="ir.ui.view" id="course_tree_view">

<field name="name">course.tree</field>

<field name="model">openacademy.course</field>

<field name="arch" type="xml">

<tree string="Course Tree">

<field name="name"/>

<field name="responsible_id"/>

</tree>

</field>

</record>

<!-- window action -->

<!--

The following tag is an action definition for a "window action",

<form string="Session Form">

<sheet>

<group>

<group string="General">

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<field name="course_id"/>

<field name="name"/>

<field name="instructor_id"/>

</group>

<group string="Schedule">

<field name="start_date"/>

<field name="duration"/>

<field name="seats"/>

</group>

</group>

</sheet>

</form>

</field>

</record>

<!-- session tree/list view -->

<record model="ir.ui.view" id="session_tree_view">

<field name="name">session.tree</field>

<field name="model">openacademy.session</field>

<field name="arch" type="xml">

<tree string="Session Tree">

<field name="name"/>

<field name="course_id"/>

</tree>

</field>

</record>

<record model="ir.actions.act_window" id="session_list_action">

<field name="name">Sessions</field>

<field name="res_model">openacademy.session</field>

练习

One2many 关联

使用 One2many 字段,修改 Session 模型,将 courses 和 sessions 关联起来。

1. 修改 Course 类

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

2. 把字段加到视图中

openacademy/models.py

responsible_id = fields.Many2one('res.users',

ondelete='set null', string="Responsible", index=True)

session_ids = fields.One2many(

'openacademy.session', 'course_id', string="Sessions")

class Session(models.Model):

openacademy/views/openacademy.xml <page string="Description">

<field name="description"/>

</page>

<page string="Sessions">

<field name="session_ids">

<tree string="Registered sessions">

<field name="name"/>

<field name="instructor_id"/>

</tree>

</field>

</page>

</notebook>

</sheet>

练习

Many2many 关联

使用 Many2many 关系字段,修改 Session 模型,把所有 session 字段和 attendee 字

段关联起来。这里 attendee 用来表示 session 的 partner,因此需要关联到内置模型

res.partner。对视图进行相应调整。

1. 修改 Session 类

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

2. 在 form 视图中增加这个字段

openacademy/models.py

instructor_id = fields.Many2one('res.partner',

string="Instructor")

course_id = fields.Many2one('openacademy.course',

ondelete='cascade', string="Course", required=True)

attendee_ids = fields.Many2many('res.partner',

string="Attendees")

openacademy/views/openacademy.xml

<field name="seats"/>

</group>

</group>

<label for="attendee_ids"/>

<field name="attendee_ids"/>

</sheet>

</form>

</field>

2.5 继承

2.5.1 模型继承

Odoo 提供了两种机制用于模块化地继承一个现有模型。

第一种继承机制允许模型修改另一个模型中的业务逻辑:

1. 增加字段

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

2. 重定义原有字段

3. 增加约束

4. 增加方法

5. 重定义原有方法

第二种继承机制(代理)将在模型中创建父类的链接,用户可以访问本模型字段达到

访问父类字段的效果。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

2.5.2 视图继承

Odoo 并不会通过重写来修改现有的视图,而是提供了视图继承,在这些视图中,子

视图的扩展内容将应用于根视图之上,并且可以添加或删除来自其父视图的内容。

扩展视图使用 inherit_id 字段来关联它的父视图,它的 arch 字段由任意数量的

xpath 元素组成,通过 xpath 元素来选择并更改其父视图的内容:

<!-- improved idea categories list -->

<record id="idea_category_list2" model="ir.ui.view">

<field name="name">id.category.list2</field>

<field name="model">idea.category</field>

<field name="inherit_id" ref="id_category_list"/>

<field name="arch" type="xml">

<!-- find field description and add the field

idea_ids after it -->

<xpath expr="//field[@name='description']" position="after">

<field name="idea_ids" string="Number of ideas"/>

</xpath>

</field>

</record>

expr

XPath 表达式,用来在父视图选择一个元素。如果匹配 0 个或多个元素,则会引发错

误。

position

可用参数如下:

inside

在匹配的元素的末尾添加 xpath 的主体。

replace

用 xpath 的内容替换匹配的元素,即替换所有节点

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

before

在匹配的元素之前插入 xpath 的内容。

after

在匹配的元素之后插入 xpath 的内容。

attributes

用 xpath 中的内容改变匹配元素的属性。

When matching a single element, the position attribute can be set directly on the

element to be found. Both inheritances below will give the same result.

如果父视图中的目标字段只存在一次,还有一种继承写法。例如:

<xpath expr="//field[@name='description']" position="after">

<field name="idea_ids" />

</xpath>

<field name="description" position="after">

<field name="idea_ids" />

</field>

练习

修改现有内容

1. 使用模型继承,修改现有的 Partner 模型:添加一个 instructor(布尔字段),以

及一个 Many2many 字段用来关联 session-partner。

2. 使用视图继承,在 partner 表单中展示这两个字段。

建议启用开发者模式来检查视图,找到它的外部 id 并写入新字段。

创建一个 openacademy/models/partner.py 文件,并在__init__.py 引用它

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

创建一个 openacademy/views/partner.xml 文件,并把它加入__manifest__.py

openacademy/__init__.py

# -*- coding: utf-8 -*-

from . import controllers

from . import models

from . import partner

openacademy/__manifest__.py

# 'security/ir.model.access.csv',

'templates.xml',

'views/openacademy.xml',

'views/partner.xml',

],

# only loaded in demonstration mode

'demo': [

openacademy/partner.py

# -*- coding: utf-8 -*-

from odoo import fields, models

class Partner(models.Model):

_inherit = 'res.partner'

# Add a new column to the res.partner model, by default

partners are not

# instructors

instructor = fields.Boolean("Instructor", default=False)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

session_ids = fields.Many2many('openacademy.session',

string="Attended Sessions", readonly=True)

openacademy/views/partner.xml

<?xml version="1.0" encoding="UTF-8"?>

<odoo>

<!-- Add instructor field to existing view -->

<record model="ir.ui.view"

id="partner_instructor_form_view">

<field name="name">partner.instructor</field>

<field name="model">res.partner</field>

<field name="inherit_id"

ref="base.view_partner_form"/>

<field name="arch" type="xml">

<notebook position="inside">

<page string="Sessions">

<group>

<field name="instructor"/>

<field name="session_ids"/>

</group>

</page>

</notebook>

</field>

</record>

<record model="ir.actions.act_window"

id="contact_list_action">

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<field name="name">Contacts</field>

<field name="res_model">res.partner</field>

<field name="view_mode">tree,form</field>

</record>

<menuitem id="configuration_menu" name="Configuration"

parent="main_openacademy_menu"/>

<menuitem id="contact_menu" name="Contacts"

parent="configuration_menu"

action="contact_list_action"/>

</odoo>

Domains 搜索域

在 Odoo 中,domains 是在模型记录上筛选条件的结果。搜索域是一个由若干标准元组

形成的列表,用于筛选出记录集的子集合。每个标准元组都是一个具有字段名、操作符和

值的三元元组。

例如,可以筛选出 Product 模型中 type 是 service、unit_price>1000 的记录集:

[('product_type', '=', 'service'), ('unit_price', '>', 1000)]

默认条件下,标准元组之间用 AND 关联。逻辑运算符&(和),|(或)和!(NOT)可以用于

显式地合并标准元组。它们在前缀位置使用(操作符在元组之前插入,而不是在中间插入)。

例如,筛选出“是 service 或 unit_price 不在 1000 至 2000 之间”的产品:

['|',

('product_type', '=', 'service'),

'!', '&',

('unit_price', '>=', 1000),

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

('unit_price', '<', 2000)]

当用户尝试在客户端中选择记录时,可以将 domain 参数添加到关系字段中,用来返回

对 domain 来说有效的记录。

练习

关系字段的搜索域

当为 session 选择一个 instructor 时,只有存在 instructor 为 true 的 instructor 才是可见

的。

openacademy/models.py

duration = fields.Float(digits=(6, 2), help="Duration in days")

seats = fields.Integer(string="Number of seats")

instructor_id = fields.Many2one('res.partner',

string="Instructor",

domain=[('instructor', '=', True)])

course_id = fields.Many2one('openacademy.course',

ondelete='cascade', string="Course", required=True)

attendee_ids = fields.Many2many('res.partner',

string="Attendees")

***A domain declared as a literal list is evaluated server-side and can't refer to dynamic

values on the right-hand side, a domain declared as a string is evaluated client-side and

allows field names on the right-hand side

练习

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

复杂 domain 条件

创建新的 partner category 模型:Teacher / Level 1 and Teacher / Level 2。

Session 的 instructor 可以是一个 instructor 或 teacher(任何级别)。

1. 修改 Session 模型的搜索域。

2. 修改 openacademy/view/partner.xml 文件用于访问 Partner 分类。

openacademy/models.py

seats = fields.Integer(string="Number of seats")

instructor_id = fields.Many2one('res.partner',

string="Instructor",

domain=['|', ('instructor', '=', True),

('category_id.name', 'ilike', "Teacher")])

course_id = fields.Many2one('openacademy.course',

ondelete='cascade', string="Course", required=True)

attendee_ids = fields.Many2many('res.partner',

string="Attendees")

openacademy/views/partner.xml

<menuitem id="contact_menu" name="Contacts"

parent="configuration_menu"

action="contact_list_action"/>

<record model="ir.actions.act_window"

id="contact_cat_list_action">

<field name="name">Contact Tags</field>

<field name="res_model">res.partner.category</field>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<field name="view_mode">tree,form</field>

</record>

<menuitem id="contact_cat_menu" name="Contact Tags"

parent="configuration_menu"

action="contact_cat_list_action"/>

<record model="res.partner.category" id="teacher1">

<field name="name">Teacher / Level 1</field>

</record>

<record model="res.partner.category" id="teacher2">

<field name="name">Teacher / Level 2</field>

</record>

</odoo>

2.6 计算字段和默认值

到目前为止,字段的值都是直接存储在数据库中并直接从数据库中检索。实际上,字

段的值算出来。在这种情况下,字段的值不是从数据库中检索出来的,而是通过调用模型

的方法实时计算的。

要创建一个计算型字段,需要为一个字段设置 compute方法。对应计算方法应该对 self

中的所有记录简单地设置该字段的值

注意

self 是一个集合

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

对象 self 是一个记录集,即,一个由有序的记录组成的集合。它支持 Python 标准集合

的所有操作,如 len(self)和 iter(self),以及 recs1 + recs2 等额外的集合操作。

可以通过迭代 self 提供了获得每个记录,其中每个记录本身都是长度为 1 的记录集。

您可以使用点表示法(如 record.name)访问/指定单个记录上的字段。

import random

from odoo import models, fields, api

class ComputedModel(models.Model):

_name = 'test.computed'

name = fields.Char(compute='_compute_name')

@api.multi

def _compute_name(self):

for record in self:

record.name = str(random.randint(1, 1e6))

2.6.1 Dependencies 依赖关系

计算字段的值通常取决与上该记录的其他字段。ORM 希望开发人员可以使用修饰符

depends()来指定那些相关的字段。当某些依赖项被修改时,ORM 会重新计算该字段计算:

from odoo import models, fields, api

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

class ComputedModel(models.Model):

_name = 'test.computed'

name = fields.Char(compute='_compute_name')

value = fields.Integer()

@api.depends('value')

def _compute_name(self):

for record in self:

record.name = "Record with value %s" % record.value

练习

计算型字段

在 Session 模型中增加被占用的椅子的 percentage 字段

在 tree 视图和 form 视图展示该字段

把这个字段放在 progress bar 中展示

1. 将计算字段添加到会话。

2. 在会话视图中显示该字段:

openacademy/models.py

course_id = fields.Many2one('openacademy.course',

ondelete='cascade', string="Course", required=True)

attendee_ids = fields.Many2many('res.partner',

string="Attendees")

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

taken_seats = fields.Float(string="Taken seats",

compute='_taken_seats')

@api.depends('seats', 'attendee_ids')

def _taken_seats(self):

for r in self:

if not r.seats:

r.taken_seats = 0.0

else:

r.taken_seats = 100.0 * len(r.attendee_ids) /

r.seats

openacademy/views/openacademy.xml

<field name="start_date"/>

<field name="duration"/>

<field name="seats"/>

<field name="taken_seats"

widget="progressbar"/>

</group>

</group>

<label for="attendee_ids"/>

<tree string="Session Tree">

<field name="name"/>

<field name="course_id"/>

<field name="taken_seats" widget="progressbar"/>

</tree>

</field>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

</record>

2.6.2 默认值

任何字段都可以有一个默认值。在字段定义中,增加 default=X 选项,其中 x 可以是一

个 Python 有效值,或者一个用于计算的函数:

name = fields.Char(default="Unknown")

user_id = fields.Many2one('res.users', default=lambda self: self.env.user)

self.env 为请求提供权限参数和其他参数:

self.env.cr 或 self._cr 是数据库的光标对象;它用于查询数据库。

self.env.uid 或 self._uid 是当前用户的数据库 id。

self.env.user 是当前用户的记录。

self.env.context 或 self.context 是上下文字典。

self.env.ref(xml_id)用于返回 xml 在数据库中的记录。

self.env[model_name]返回给定模型的空实例集合。

练习

默认值

将 start_date 的默认值设置为今天。

在 Session 中添加一个字段 active,并在默认情况下设置为 True。

openacademy/models.py

_name = 'openacademy.session'

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

name = fields.Char(required=True)

start_date = fields.Date(default=fields.Date.today)

duration = fields.Float(digits=(6, 2), help="Duration in days")

seats = fields.Integer(string="Number of seats")

active = fields.Boolean(default=True)

instructor_id = fields.Many2one('res.partner',

string="Instructor",

domain=['|', ('instructor', '=', True),

openacademy/views/openacademy.xml

<field name="course_id"/>

<field name="name"/>

<field name="instructor_id"/>

<field name="active"/>

</group>

<group string="Schedule">

<field name="start_date"/>

Odoo 有内置规则,可以使字段设置为假的 invisible。

2.7 ONCHANGE

“onchange”机制为客户端接口提供了一种方法,当用户在字段中赋值时,在不更新

数据库字段的情况下,更新表单字段的值。

例如,假设一个模型有三个字段amount, unit_price和price。现在你希望在amount,

unit_price 被修改时更新表单的 pirce。为了实现这一点,定义以下方法,其中传入的 self

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

表示表单视图中的记录,然后用 onchange()修饰符传入触发该方法的字段。这样你对这些

字段的任何改变都将调用这个方法上,并在页面上显示出来。

<!-- content of form view -->

<field name="amount"/>

<field name="unit_price"/>

<field name="price" readonly="1"/>

# onchange handler

@api.onchange('amount', 'unit_price')

def _onchange_price(self):

# set auto-changing field

self.price = self.amount * self.unit_price

# Can optionally return a warning and domains

return {

'warning': {

'title': "Something bad happened",

'message': "It was very bad indeed",

}

}

}

对于计算型字段,onchange 可以在页面上起到相辅相成的效果:先在页面中调用

onchange 更新数据,然后在调用 compute 方法更新数据库。

练习

警告

添 加 一 个 显 式的 onchange 方 法 来 警示 无 效 的值 , 比 如 一 个 负 数的 座位 , 或

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

participants 比 seats 多的情况。

openacademy/models.py

r.taken_seats = 0.0

else:

r.taken_seats = 100.0 * len(r.attendee_ids) /

r.seats

@api.onchange('seats', 'attendee_ids')

def _verify_valid_seats(self):

if self.seats < 0:

return {

'warning': {

'title': "Incorrect 'seats' value",

'message': "The number of available seats may

not be negative",

},

}

if self.seats < len(self.attendee_ids):

return {

'warning': {

'title': "Too many attendees",

'message': "Increase seats or remove excess

attendees",

},

}

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

2.8 模型约束

Odoo 提供了两种方法来实现自动验证: Python constraints(Python 约束)和 SQL

constraints(Sql 约束)。

Python constraints 通过 constrains ()修饰方法,使用记录集调用。需要在修饰符指定

需要校对的字段,以便当其中的某个字段被变更时约束方法可以自动执行。约束方法硬砸

死不满足条件时抛出异常:

from odoo.exceptions import ValidationError

@api.constrains('age')

def _check_something(self):

for record in self:

if record.age > 20:

raise ValidationError("Your record is too old: %s" %

record.age)

# all records passed the test, don't return anything

练习

添加 Python 约束

添加一个约束用于检查 instructor 是否参与了他自己的 Session

openacademy/models.py

# -*- coding: utf-8 -*-

from odoo import models, fields, api, exceptions

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

class Course(models.Model):

_name = 'openacademy.course'

'message': "Increase seats or remove excess

attendees",

},

}

@api.constrains('instructor_id', 'attendee_ids')

def _check_instructor_not_in_attendees(self):

for r in self:

if r.instructor_id and r.instructor_id in

r.attendee_ids:

raise exceptions.ValidationError("A session's

instructor can't be an attendee")

SQL 约束是通过模型属性_sql_constraints 定义的。通过传入三元字符串元祖(name,

sql_definition, message),其中 name 是一个有效的 SQL 约束名称,sql_definition 是一个有

效的数据库验证表达式,message 是错误消息。

练习

添加 SQL 约束

在 PostgreSQL's documentation 的帮助下,添加以下约束:

1. 检查 course 的 description 和 title 是否不同。

2. course 的名称不能重复。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

openacademy/models.py

session_ids = fields.One2many(

'openacademy.session', 'course_id', string="Sessions")

_sql_constraints = [

('name_description_check',

'CHECK(name != description)',

"The title of the course should not be the description"),

('name_unique',

'UNIQUE(name)',

"The course title must be unique"),

]

class Session(models.Model):

_name = 'openacademy.session'

练习

练习 6 – 增加复制功能

因为我们已经添加了对 name 的唯一约束,所以不能再使用 duplicate 方法来对记录创

建副本。

重新实现你的赋值方法,使其赋值记录的其他字段,并把新记录的 name 字段改为[原

名称]的副本。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

openacademy/models.py

session_ids = fields.One2many(

'openacademy.session', 'course_id', string="Sessions")

@api.multi

def copy(self, default=None):

default = dict(default or {})

copied_count = self.search_count(

[('name', '=like', u"Copy of {}%".format(self.name))])

if not copied_count:

new_name = u"Copy of {}".format(self.name)

else:

new_name = u"Copy of {} ({})".format(self.name,

copied_count)

default['name'] = new_name

return super(Course, self).copy(default)

_sql_constraints = [

('name_description_check',

'CHECK(name != description)',

2.9 进阶视图

2.9.1 Tree 视图

可以通过一些补充性质的实行来自定义 tree 视图的表现效果。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

decoration-{$name}

允许根据相应的记录的属性改变行文本的样式。

对于每个记录,系统将指定属性传入上下文,并根据上下文判断是否符合条件,如果

是,将样式应用到该行中。除了指定字段,上下文还包括 uid(当前用户的 id)和

current_date(年-月-日格式的当前日期)

{$name} 可以是 bf (font-weight: bold), it (font-style: italic), 或者任

是 bootstrap contextual color (danger, info, muted, primary, success or warning).

<tree string="Idea Categories" decoration-info="state=='draft'"

decoration-danger="state=='trashed'">

<field name="name"/>

<field name="state"/>

</tree>

editable

可选项:"top" 或者 "bottom"。使用户可以直接在 tree 视图上编辑记录(而不是 form

视图),value 值是新行在 tree 视图出现的位置。

练习

列表着色

修改会话 tree 视图,使持续时间少于 5 天 session 显示蓝色,而持续超过 15 天的则

显示红色。

修改会话 tree 视图:

openacademy/views/openacademy.xml

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<field name="name">session.tree</field>

<field name="model">openacademy.session</field>

<field name="arch" type="xml">

<tree string="Session Tree" decoration-

info="duration&lt;5" decoration-danger="duration&gt;15">

<field name="name"/>

<field name="course_id"/>

<field name="duration" invisible="1"/>

<field name="taken_seats" widget="progressbar"/>

</tree>

</field>

2.9.2 Calendars 视图

使用日程表视图的形式展示记录。它们的根元素是<calendar>,它们最常见的属性是:

color:

指定一个模型字段用于区别记录颜色。颜色会自动地分布到事件中,但是 color 的值

相同的记录将被赋予相同的颜色。

date_start:

记录在日历中对应的起始时间字段。

date_stop (选填)

记录在日历中对应的结束时间字段。

字段(在事件格中展示的字段)

calendar string="Ideas" date_start="invent_date"

color="inventor_id">

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<field name="name"/>

</calendar>

练习

日程表视图

在 Session 模型中增加一个日程表视图,使用户可以看到与 Open Academy 相关的事件。

增加一个 end_date 字段,由 start_date 和 duration 计算得出。

再写一个反函数使得这个字段可以被前端写入,并且允许通过拖拽移动视图中的

session。

openacademy/models.py

# -*- coding: utf-8 -*-

from datetime import timedelta

from odoo import models, fields, api, exceptions

class Course(models.Model):

attendee_ids = fields.Many2many('res.partner',

string="Attendees")

taken_seats = fields.Float(string="Taken seats",

compute='_taken_seats')

end_date = fields.Date(string="End Date", store=True,

compute='_get_end_date', inverse='_set_end_date')

@api.depends('seats', 'attendee_ids')

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

def _taken_seats(self):

},

}

@api.depends('start_date', 'duration')

def _get_end_date(self):

for r in self:

if not (r.start_date and r.duration):

r.end_date = r.start_date

continue

# Add duration to start_date, but: Monday + 5 days =

Saturday, so

# subtract one second to get on Friday instead

start = fields.Datetime.from_string(r.start_date)

duration = timedelta(days=r.duration, seconds=-1)

r.end_date = start + duration

def _set_end_date(self):

for r in self:

if not (r.start_date and r.end_date):

continue

# Compute the difference between dates, but: Friday -

Monday = 4 days,

# so add one day to get 5 days instead

start_date = fields.Datetime.from_string(r.start_date)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

end_date = fields.Datetime.from_string(r.end_date)

r.duration = (end_date - start_date).days + 1

@api.constrains('instructor_id', 'attendee_ids')

def _check_instructor_not_in_attendees(self):

for r in self:

openacademy/views/openacademy.xml

</field>

</record>

<!-- calendar view -->

<record model="ir.ui.view" id="session_calendar_view">

<field name="name">session.calendar</field>

<field name="model">openacademy.session</field>

<field name="arch" type="xml">

<calendar string="Session Calendar"

date_start="start_date" date_stop="end_date" color="instructor_id">

<field name="name"/>

</calendar>

</field>

</record>

<record model="ir.actions.act_window"

id="session_list_action">

<field name="name">Sessions</field>

<field name="res_model">openacademy.session</field>

<field name="view_type">form</field>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<field name="view_mode">tree,form,calendar</field>

</record>

<menuitem id="session_menu" name="Sessions"

2.9.3 Search 视图

搜索视图中的<field>元素可以通过 filter_domain 去重写系统对这个字段的搜索逻辑。

在指定的 domain 中,self 表示任何用户在页面输入的值。比如下面这个例子,可以用来同

时在 name 和 description 进行搜索。

搜索视图也可以包含<filter>元素,用于预设自定义搜索条件。filters 必须包含下的

参数:

domain:

对当前搜索增加指定 domain 条件

context:

对当前搜索增加指定一些上下文。使用关键字 group_by 可以对指定字段聚合查询结果。

< <search string="Ideas">

<field name="name"/>

<field name="description" string="Name and description"

filter_domain="['|', ('name', 'ilike', self),

('description', 'ilike', self)]"/>

<field name="inventor_id"/>

<field name="country_id" widget="selection"/>

<filter name="my_ideas" string="My Ideas"

domain="[('inventor_id', '=', uid)]"/>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<group string="Group By">

<filter name="group_by_inventor" string="Inventor"

context="{'group_by': 'inventor_id'}"/>

</group>

</search>

action 可 以 通 过 设 置 context 字 段 传 入 搜 索 的 默 认 值 : context 的 键

search_default_field_name 将对搜索视图中的 field_name初始化。搜索筛选器的选填属性

name 必须已经定义了,且通过输入布尔值来启用/禁用筛选。

练习

搜索视图

添加一个按钮用于筛选出当前用户负责的 courses。使其默认进行筛选。

添加一个按钮将结果按用户聚合起来。

openacademy/views/openacademy.xml

<search>

<field name="name"/>

<field name="description"/>

<filter name="my_courses" string="My Courses"

domain="[('responsible_id', '=',

uid)]"/>

<group string="Group By">

<filter name="by_responsible"

string="Responsible"

context="{'group_by':

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

'responsible_id'}"/>

</group>

</search>

</field>

</record>

<field name="res_model">openacademy.course</field>

<field name="view_type">form</field>

<field name="view_mode">tree,form</field>

<field name="context"

eval="{'search_default_my_courses': 1}"/>

<field name="help" type="html">

<p class="oe_view_nocontent_create">Create the first

course

</p>

2.9.4 Gantt

警告

Gantt 视图需要 Odoo 企业版环境.模块中显示 web_gantt 模块。

水平条形图一般用来显示项目的规划和进展,它的根元素是<gantt>

<gantt string="Ideas"

date_start="invent_date"

date_stop="date_finished"

progress="progress"

default_group_by="inventor_id" />

练习

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

条形图

增加一条形图使用户可以查看 open_academy 模型相链接的 Session 进度。session 应

该根据 instructor 聚合。

创建一个计算型字段用来计算 session 的持续时间。

添加 gantt 视图,并把 gantt 图加到 Session 的 action 中。

openacademy/models.py

end_date = fields.Date(string="End Date", store=True,

compute='_get_end_date', inverse='_set_end_date')

hours = fields.Float(string="Duration in hours",

compute='_get_hours', inverse='_set_hours')

@api.depends('seats', 'attendee_ids')

def _taken_seats(self):

for r in self:

end_date = fields.Datetime.from_string(r.end_date)

r.duration = (end_date - start_date).days + 1

@api.depends('duration')

def _get_hours(self):

for r in self:

r.hours = r.duration * 24

def _set_hours(self):

for r in self:

r.duration = r.hours / 24

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

@api.constrains('instructor_id', 'attendee_ids')

def _check_instructor_not_in_attendees(self):

for r in self:

openacademy/views/openacademy.xml

</field>

</record>

<record model="ir.ui.view" id="session_gantt_view">

<field name="name">session.gantt</field>

<field name="model">openacademy.session</field>

<field name="arch" type="xml">

<gantt string="Session Gantt" color="course_id"

date_start="start_date" date_delay="hours"

default_group_by='instructor_id'>

<!-- <field name="name"/> this is not required

after Odoo 10.0 -->

</gantt>

</field>

</record>

<record model="ir.actions.act_window"

id="session_list_action">

<field name="name">Sessions</field>

<field name="res_model">openacademy.session</field>

<field name="view_type">form</field>

<field name="view_mode">tree,form,calendar,gantt</field>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

</record>

<menuitem id="session_menu" name="Sessions"

2.9.5 Graph views 图视图

graph 视图允许对记录进行聚合概述和分析,其根元素是 <graph>。

Pivot 视图(element <pivot>)是一个多维表,允许根据选择的维度来获得正确的聚合数

据,然后形成图形化的概述。graph 视图的内容定义与 pivot 相同

graph 视图有 4 种显示模式,由 type 属性决定。

默认情况下,聚合条是并排的,它们可以在使用@stack ="True"在<graph>

二维视图

二维视图包含,带有强制的@type 属性,取值如下:

Bar (默认情况)

条形图,第一个维度用于在水平轴上进行分组,其他维度定义每个组内的聚合条。

默认的条形图使并排的,它们可以在<graph> 中加入 stacked="True"进行堆叠。

Line

二维条形图

Pie

二维饼图。

Graph 视图包含 <field>属性,其中的 type 参数是必填的,可用参数如下:

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

row (default)

graph 视图应该根据这个字段进行分组。

measure

graph 视图应该根据这个字段进行聚合。

<graph string="Total idea score by Inventor">

<field name="inventor_id"/>

<field name="score" type="measure"/>

</graph>

警告

graph 视图将直接对数据库发送请求,它们不能处理 Store=False 的计算型字段。

练习

Graph 视图

为 Session 添加一个 graph 视图,用条形图展示每个 courses 的参与人数。

1. 增加 attendees 为一个储存在数据库中的计算型字段。

2. 增加相关视图。

openacademy/models.py

hours = fields.Float(string="Duration in hours",

compute='_get_hours', inverse='_set_hours')

attendees_count = fields.Integer(

string="Attendees count", compute='_get_attendees_count',

store=True)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

@api.depends('seats', 'attendee_ids')

def _taken_seats(self):

for r in self:

for r in self:

r.duration = r.hours / 24

@api.depends('attendee_ids')

def _get_attendees_count(self):

for r in self:

r.attendees_count = len(r.attendee_ids)

@api.constrains('instructor_id', 'attendee_ids')

def _check_instructor_not_in_attendees(self):

for r in self:

openacademy/views/openacademy.xml

</field>

</record>

<record model="ir.ui.view"

id="openacademy_session_graph_view">

<field name="name">openacademy.session.graph</field>

<field name="model">openacademy.session</field>

<field name="arch" type="xml">

<graph string="Participations by Courses">

<field name="course_id"/>

<field name="attendees_count" type="measure"/>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

</graph>

</field>

</record>

<record model="ir.actions.act_window"

id="session_list_action">

<field name="name">Sessions</field>

<field name="res_model">openacademy.session</field>

<field name="view_type">form</field>

<field

name="view_mode">tree,form,calendar,gantt,graph</field>

</record>

<menuitem id="session_menu" name="Sessions"

2.9.6 Kanban 视图

用于组织任务、生产流程等…

根元素是<kanban>。

看板视图在若干列中用卡片的形式展示数据。每个卡片代表一个记录,每个列都代表

一个聚合字段的值。

例如,项目任务可以按阶段组织(每一列是一个阶段),或者由负责的组织(每一列是一

个用户),等等。

看板视图将每个卡片的结构定义为表单元素(包括基本的 HTML 元素)和 QWeb 的组合。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

练习

看板视图

增加一个看板视图,用于展示按 course 分组的 session

1.在 Session 模型增加一个字段 color

2.增加一个看板视图,并更新至 action

openacademy/models.py

duration = fields.Float(digits=(6, 2), help="Duration in days")

seats = fields.Integer(string="Number of seats")

active = fields.Boolean(default=True)

color = fields.Integer()

instructor_id = fields.Many2one('res.partner',

string="Instructor",

domain=['|', ('instructor', '=', True),

openacademy/views/openacademy.xml

</field>

</record>

<record model="ir.ui.view"

id="view_openacad_session_kanban">

<field name="name">openacad.session.kanban</field>

<field name="model">openacademy.session</field>

<field name="arch" type="xml">

<kanban default_group_by="course_id">

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<field name="color"/>

<templates>

<t t-name="kanban-box">

<div

t-attf-

class="oe_kanban_color_{{kanban_getcolor(record.color.raw_value)}}

oe_kanban_global_click_edit oe_semantic_html_override

oe_kanban_card

{{record.group_fancy==1 ? 'oe_kanban_card_fancy' : ''}}">

<div class="oe_dropdown_kanban">

<!-- dropdown menu -->

<div class="oe_dropdown_toggle">

<i class="fa fa-bars fa-

lg"/>

<ul

class="oe_dropdown_menu">

<li>

<a

type="delete">Delete</a>

</li>

<li>

<ul

class="oe_kanban_colorpicker"

data-

field="color"/>

</li>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

</ul>

</div>

<div class="oe_clear"></div>

</div>

<div t-attf-

class="oe_kanban_content">

<!-- title -->

Session name:

<field name="name"/>

<br/>

Start date:

<field name="start_date"/>

<br/>

duration:

<field name="duration"/>

</div>

</div>

</t>

</templates>

</kanban>

</field>

</record>

<record model="ir.actions.act_window"

id="session_list_action">

<field name="name">Sessions</field>

<field name="res_model">openacademy.session</field>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<field name="view_type">form</field>

<field

name="view_mode">tree,form,calendar,gantt,graph,kanban</field>

</record>

<menuitem id="session_menu" name="Sessions"

2.10 SECURITY

访问权限机制用于实现井然有序的安全策略。

2.10.1 基于 group 的访问权限机制

group 可以通过模型 res.groups 来创建。通常我们可以通过对菜单的相关配置来控制

相应的访问权限(这样用户就无法直接访问相应记录)。然而,绕开菜单,对象仍然能被

用户间接地访问,因此对象层级的权限(增删改查)必须由 group 来控制。一般他们由

CSV 文档插入至模型中。我们同样可以在视图或者字段赔指数型,来限制相应的访问权限。

2.10.2 访问权限

访问权限本质是 ir.model.access 的记录。每个访问权都与一个模型、一个权限组(或

者全局权限组),以及一组权限(增删改查)相关。这样的访问权限通常是由一个以其模型

命名的 CSV 文件创建的:ir.model.access.csv。

id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,per

m_unlink

access_idea_idea,idea.idea,model_idea_idea,base.group_user,1,1,1,0

access_idea_vote,idea.vote,model_idea_vote,base.group_user,1,1,1,0

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

练习

通过 odoo 接口增加访问权限控制

创建一个新用户“John Smith”。创建一个权限组"OpenAcademy / Session Read",

允许对 Session 模型进行查操作。

1. 通过 Settings ‣ Users ‣ Users 增加一个新用户 John Smith

2. 通过 Settings ‣ Users ‣ Groups 增加一个新权限组 session_read,给予

查权限。

3. 编辑 John Smith,使他加入 session_read

4. 用 John Smith 身份登录检查权限控制是否正确

练习

通过数据文件为模块增加权限控制,

使用数据文件,

创建权限组 OpenAcademy / Manager,给予 OpenAcademy 模块的完整权限。

使 Session 和 Course 对所有用户可查

1. 创建一个文件 openacademy/security/security.xml 用于放置

OpenAcademy 的管理员权限组。

2. 使用权限编辑 openacademy/security/ir.model.access.csv

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

3. 最后把新文件更新到 openacademy/__manifest__.py 中

openacademy/__manifest__.py

# always loaded

'data': [

'security/security.xml',

'security/ir.model.access.csv',

'templates.xml',

'views/openacademy.xml',

'views/partner.xml',

openacademy/security/ir.model.access.csv

id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,per

m_unlink

course_manager,course

manager,model_openacademy_course,group_manager,1,1,1,1

session_manager,session

manager,model_openacademy_session,group_manager,1,1,1,1

course_read_all,course all,model_openacademy_course,,1,0,0,0

session_read_all,session all,model_openacademy_session,,1,0,0,0

openacademy/security/security.xml

<odoo>

<record id="group_manager" model="res.groups">

<field name="name">OpenAcademy / Manager</field>

</record>

</odoo>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

2.10.3 记录规则

记录规则控制了指定模型的指定记录的操作权限。规则是 ir.rule 的一个记录,它与

以下对象相关:一个模型,若干个权限组(many2many 字段),限制规则和搜索域。域搜索

域决定了记录规则控制了哪些记录的操作权限。

这里有一个例子,它可以防止用户删除不处于 cancel 状态的 lead。请注意,字段

groups 的值必须遵循与 ORM 的 write()方法相同的规则。

<record id="delete_cancelled_only" model="ir.rule">

<field name="name">Only cancelled leads may be deleted</field>

<field name="model_id" ref="crm.model_crm_lead"/>

<field name="groups" eval="[(4,

ref('sales_team.group_sale_manager'))]"/>

<field name="perm_read" eval="0"/>

<field name="perm_write" eval="0"/>

<field name="perm_create" eval="0"/>

<field name="perm_unlink" eval="1" />

<field name="domain_force">[('state','=','cancel')]</field>

</record>

练习

记录规则

为 Course 增加一个记录规则和一个权限组"OpenAcademy / Manager",用于限制对

course 的 responsible 字段的改和查权限。如果 course 不存在 responsible,所有该权限组

的用户都可以修改它。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

在 openacademy/security/security.xml 创建一个新的记录规则。

openacademy/security/security.xml

<record id="group_manager" model="res.groups">

<field name="name">OpenAcademy / Manager</field>

</record>

<record id="only_responsible_can_modify" model="ir.rule">

<field name="name">Only Responsible can modify

Course</field>

<field name="model_id"

ref="model_openacademy_course"/>

<field name="groups" eval="[(4,

ref('openacademy.group_manager'))]"/>

<field name="perm_read" eval="0"/>

<field name="perm_write" eval="1"/>

<field name="perm_create" eval="0"/>

<field name="perm_unlink" eval="1"/>

<field name="domain_force">

['|', ('responsible_id','=',False),

('responsible_id','=',user.id)]

</field>

</record>

</odoo>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

2.11 WIZARD 向导

Wizard 使用动态的表单与用户在对话框上进行互动。通常 Wizard 扩展的父类是

TransientModel 而不是 Model ,其特性如下:

Wizard 记录不应被持久化,它们会在一段时候后自动从数据库中删除。这就是为什么

它们被称为 transient。

一般 Wizard 模型不需要设置显式的访问权限:用户拥有向导记录的所有权限。

向导记录可以通过 Many2one 字段引用(非)持久化记录,但持久化记录不能通过

Many2one 字段引用向导记录。

我们希望创建一个向导,该向导允许用户迅速地为特定/若干的会话创建参与者。

练习

定义 Wizard

1.建立一个 wizard 模型,加入一个 Many2one 字段用于引用 Session 模型,以及一个

Many2many 字段用于引用 Partner 模型。

2.更新至 openacademy/wizard.py:

openacademy/__init__.py

from . import controllers

from . import models

from . import partner

from . import wizard

openacademy/wizard.py

# -*- coding: utf-8 -*-

from odoo import models, fields, api

class Wizard(models.TransientModel):

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

_name = 'openacademy.wizard'

session_id = fields.Many2one('openacademy.session',

string="Session", required=True)

attendee_ids = fields.Many2many('res.partner', string="Attendees")

2.11.1 启动 Wizard

Wizard 由 ir.actions.act_window 启动,其中需要将字段 target 设置为 new。这个

action 在弹框中显示 Wizard 视图。一般通过菜单项启动 Wizard。

还有一种方式来启动 wizard:仍然使用之前提到的 ir.actions.act_window 记录,但

是可以通过定义字段 src_model 用于指定该 wizard 对于什么模型是有效的,这个 wizard 将

在该模型的 Action 菜单栏下出现。上述的 action 在系统中在 act_window 标签中定义。

<act_window id="launch_the_wizard"

name="Launch the Wizard"

src_model="context.model.name"

res_model="wizard.model.name"

view_mode="form"

target="new"

key2="client_action_multi"/>

向导调用常规视图,其中的按钮的属性可以写 special="cancel"。这样的按钮可以用

来关闭向导窗口而不保存。

1 为向导定义一个表单视图。

2 添加在会话模型上下文中启动它的操作。

3 所示。在向导中定义会话字段的默认值;使用上下文参数 self。检索当前会话的上下

文。

练习

启动向导

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

1. 定义一个 wizard 的 form 视图

2. 把它加到 Session 的 Action 菜单中

3. 为 wizard 中的 session 字段定义初始值,使用 self._context 来获取当前的

session

openacademy/wizard.py

class Wizard(models.TransientModel):

_name = 'openacademy.wizard'

def _default_session(self):

return self.env['openacademy.session'].browse(self._context.get('active_id'))

session_id = fields.Many2one('openacademy.session',

string="Session", required=True, default=_default_session)

attendee_ids = fields.Many2many('res.partner', string="Attendees")

openacademy/views/openacademy.xml

parent="openacademy_menu"

action="session_list_action"/>

<record model="ir.ui.view" id="wizard_form_view">

<field name="name">wizard.form</field>

<field name="model">openacademy.wizard</field>

<field name="arch" type="xml">

<form string="Add Attendees">

<group>

<field name="session_id"/>

<field name="attendee_ids"/>

</group>

</form>

</field>

</record>

<act_window id="launch_session_wizard"

name="Add Attendees"

src_model="openacademy.session"

res_model="openacademy.wizard"

view_mode="form"

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

target="new"

key2="client_action_multi"/>

</odoo>

练习

登记与会者

在 wizard 视图中增加按钮,调用方法用来给指定的 session 增加参与者。

openacademy/views/openacademy.xml

<field name="session_id"/>

<field name="attendee_ids"/>

</group>

<footer>

<button name="subscribe" type="object"

string="Subscribe" class="oe_highlight"/>

or

<button special="cancel" string="Cancel"/>

</footer>

</form>

</field>

</record>

openacademy/wizard.py

session_id = fields.Many2one('openacademy.session',

string="Session", required=True, default=_default_session)

attendee_ids = fields.Many2many('res.partner', string="Attendees")

@api.multi

def subscribe(self):

self.session_id.attendee_ids |= self.attendee_ids

return {}

练习

将与会者登记到多个会议。

修改向导模型,使参与者可以注册到多个会话。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

openacademy/views/openacademy.xml

<field name="arch" type="xml">

<form string="Add Attendees">

<group>

<field name="session_ids"/>

<field name="attendee_ids"/>

</group>

<footer>

openacademy/wizard.py

class Wizard(models.TransientModel):

_name = 'openacademy.wizard'

def _default_sessions(self):

return self.env['openacademy.session'].browse(self._context.get('active_ids

'))

session_ids = fields.Many2many('openacademy.session',

string="Sessions", required=True, default=_default_sessions)

attendee_ids = fields.Many2many('res.partner', string="Attendees")

@api.multi

def subscribe(self):

for session in self.session_ids:

session.attendee_ids |= self.attendee_ids

return {}

2.12 国际化

每个模块可以在 i18n 目录中,创建 LANG.po 文件提供相应的翻译文本。其中 LANG

是语言的地区代码,如有必要,将是语言+国家的组合形式(例如,pt.po 或 pt_BR.po)。对

于 Odoo 所有的可用语言,翻译将自动加载。

通常,开发人员使用英语当创建一个模块,然后使用 Odoo 的 gettext pot export 特性

来到处模块术语((Settings ‣ Translations ‣ Import/Export ‣ Export Translation )。这样

可以创建模块模板文件,然后推导出翻译PO文件。许多 IDE都有用于编辑和合并PO/POT

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

文件的插件或模式。

Odoo 生成的可移植对象文件来自于 Transifex,这使得翻译变得更为容易

|- idea/ # The module directory

|- i18n/ # Translation files

| - idea.pot # Translation Template (exported from Odoo)

| - fr.po # French translation

| - pt_BR.po # Brazilian Portuguese translation

| (...)

在默认情况下,Odoo 的 POT export 只在 XML 文件或 python 代码中的 field 定义内提取

标签,但是任何 Python 字符串都可以将其放在 odoo._()内进行翻译(例如_(“Label”))。

练习

翻译一个模块

为你的 Odoo 选择另一种语言,使用 Odoo 提供的机制翻译你的模块。

1.创建路径 openacademy/i18n/

2.安装一个语言( Administration ‣ Translations ‣ Load an Official Translation)

3.同步翻译术语(Administration ‣ Translations ‣ Application Terms ‣ Synchronize Translations)

4.创建一个模板翻译文件用于导出( Administration ‣ Translations -> Import/Export ‣ Export

Translation)。将其保存在 inopenacademy / i18n /中。

5.打开导出的翻译文件(使用一般的文本编辑器或专用的 PO-file 编辑器,例如 POEdit 并翻

译缺失的术语。

6.在 models.py,使用 odoo._()翻译剩下的语句。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

7.重复步骤 3 - 6

openacademy/models.py

# -*- coding: utf-8 -*-

from datetime import timedelta

from odoo import models, fields, api, exceptions, _

class Course(models.Model):

_name = 'openacademy.course'

default = dict(default or {})

copied_count = self.search_count(

[('name', '=like', _(u"Copy of {}%").format(self.name))])

if not copied_count:

new_name = _(u"Copy of {}").format(self.name)

else:

new_name = _(u"Copy of {} ({})").format(self.name, copied_count)

default['name'] = new_name

return super(Course, self).copy(default)

if self.seats < 0:

return {

'warning': {

'title': _("Incorrect 'seats' value"),

'message': _("The number of available seats may not be negative

"),

},

}

if self.seats < len(self.attendee_ids):

return {

'warning': {

'title': _("Too many attendees"),

'message': _("Increase seats or remove excess attendees"),

},

}

def _check_instructor_not_in_attendees(self):

for r in self:

if r.instructor_id and r.instructor_id in r.attendee_ids:

raise exceptions.ValidationError(_("A session's instructor can't be a

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

n attendee"))

2.13 报表

2.13.1 打印的报表

Odoo 11.0 使用基于 QWeb、Twitter Bootstrap 和 Wkhtmltopdf 的报表引擎。

报告通常由两部分组成:

ir.actions.report,使用<report> 标签,可以设置报表的各种参数。

• <report

• id="account_invoices"

• model="account.invoice"

• string="Invoices"

• report_type="qweb-pdf"

• name="account.report_invoice"

• file="account.report_invoice"

• attachment_use="True"

• attachment="(object.state in ('open','paid')) and

• ('INV'+(object.number or '').replace('/','')+'.pdf')"

• />

一个标准的报表的 QWeb 视图

• <t t-call="web.html_container">

• <t t-foreach="docs" t-as="o">

• <t t-call="web.external_layout">

• <div class="page">

• <h2>Report title</h2>

• </div>

• </t>

• </t>

• </t>

• the standard rendering context provides a number of elements,

the most

• important being:

• ``docs``

• the records for which the report is printed

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

• ``user``

• the user printing the report

因为报告本质还是一个网页页面,它们可以通过一个 URL 和若干参数来访问这个 URL。

例 如 HTML 版 本 的 发 票 报 告 可 以 通 过

http://localhost:8069/report/html/account.report_invoice/1 预览(如果 account 模块已安装),

而 PDF 版本可以通过 http://localhost:8069/report/pdf/account.report_invoice/1。

注意

如果您的 PDF 报告显示缺少样式(即文本相同但样式/布局与 html 版本不同),可能您

的 wkhtmltopdf 进程无法连接到您的 web 服务器(去下载那些样式)。

如果您检查您的服务器日志,并看到日志里提到了那些没有下载成功的 CSS 样式,那

么这无疑是问题所在。

wkhtmltopdf 进程将使用 web.base.url 系统参数作为所有链接文件的根路径,但是

每当管理员登录时,该参数都会自动更新。如果您的服务器使用了代理工具,那么这个进

程就无法连接你的服务器。您可以通过添加以下一些系统参数来解决这个问题:

1.report.url

手动关联一个可连接的服务器地址(例如 http://localhost:8069)。

2.web.base.url.freeze

如果为 True,将会停止 web.base.url 的自动更新

练习

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

为 Session 创建一个报表

对于每个 session,应当展示该 session 的 name, start, end 和出席的 atendee

openacademy/__manifest__.py

'templates.xml',

'views/openacademy.xml',

'views/partner.xml',

'reports.xml',

],

# only loaded in demonstration mode

'demo': [

openacademy/reports.xml

<odoo>

<report

id="report_session"

model="openacademy.session"

string="Session Report"

name="openacademy.report_session_view"

file="openacademy.report_session"

report_type="qweb-pdf" />

<template id="report_session_view">

<t t-call="web.html_container">

<t t-foreach="docs" t-as="doc">

<t t-call="web.external_layout">

<div class="page">

<h2 t-field="doc.name"/>

<p>From <span t-field="doc.start_date"/> to <span t-field="do

c.end_date"/></p>

<h3>Attendees:</h3>

<ul>

<t t-foreach="doc.attendee_ids" t-as="attendee">

<li><span t-field="attendee.name"/></li>

</t>

</ul>

</div>

</t>

</t>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

</t>

</template>

</odoo>

2.13.2 仪表盘

练习

定义一个仪表板

定义一个包含 graph 视图、calendar 视图、courses 的 list 视图(自动关联一个 form 视

图)的仪表板。可以通过菜单访问这个仪表盘,并且在选择 OpenAcademy 主菜单时自动显

示在用户的客户端中。

1.创建一个文件 openacademy/views/session_board.xml。它应该包含 board 视图、

引用那些视图的 actions,以及应用仪表板的 action,把这个 action 放到主菜单中。可用

的的仪表板样式为 1, 1-1, 1-2, 2-1 和 1-1-1

2.更新至 openacademy/__manifest__.py 文件。

openacademy/__manifest__.py

'version': '0.1',

# any module necessary for this one to work correctly

'depends': ['base', 'board'],

# always loaded

'data': [

'templates.xml',

'views/openacademy.xml',

'views/partner.xml',

'views/session_board.xml',

'reports.xml',

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

],

# only loaded in demonstration mode

openacademy/views/session_board.xml

<?xml version="1.0"?>

<odoo>

<record model="ir.actions.act_window" id="act_session_graph">

<field name="name">Attendees by course</field>

<field name="res_model">openacademy.session</field>

<field name="view_type">form</field>

<field name="view_mode">graph</field>

<field name="view_id"

ref="openacademy.openacademy_session_graph_view"/>

</record>

<record model="ir.actions.act_window" id="act_session_calendar">

<field name="name">Sessions</field>

<field name="res_model">openacademy.session</field>

<field name="view_type">form</field>

<field name="view_mode">calendar</field>

<field name="view_id" ref="openacademy.session_calendar_view"/>

</record>

<record model="ir.actions.act_window" id="act_course_list">

<field name="name">Courses</field>

<field name="res_model">openacademy.course</field>

<field name="view_type">form</field>

<field name="view_mode">tree,form</field>

</record>

<record model="ir.ui.view" id="board_session_form">

<field name="name">Session Dashboard Form</field>

<field name="model">board.board</field>

<field name="type">form</field>

<field name="arch" type="xml">

<form string="Session Dashboard">

<board style="2-1">

<column>

<action

string="Attendees by course"

name="%(act_session_graph)d"

height="150"

width="510"/>

<action

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

string="Sessions"

name="%(act_session_calendar)d"/>

</column>

<column>

<action

string="Courses"

name="%(act_course_list)d"/>

</column>

</board>

</form>

</field>

</record>

<record model="ir.actions.act_window" id="open_board_session">

<field name="name">Session Dashboard</field>

<field name="res_model">board.board</field>

<field name="view_type">form</field>

<field name="view_mode">form</field>

<field name="usage">menu</field>

<field name="view_id" ref="board_session_form"/>

</record>

<menuitem

name="Session Dashboard" parent="base.menu_reporting_dashboard"

action="open_board_session"

sequence="1"

id="menu_board_session"/>

</odoo>

2.14 WEBSERVICES

web-service 模块为所有的 web-service 提供了一个公共接口:

• XML-RPC

• JSON-RPC

业务对象也可以通过分布式对象机制访问,它们都可以通过客户端接口进行修改。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

Odoo 可以通过 XML-RPC/JSON-RPC 接口访问,这两个接口使用的类库在很多语言

都有用到。

2.14.1 XML-RPC Library

以下的例子是用 Python3 的标准类库 xmlrpc.client 访问 Odoo 服务器:

import xmlrpc.client

root = 'http://%s:%d/xmlrpc/' % (HOST, PORT)

uid = xmlrpc.client.ServerProxy(root + 'common').login(DB, USER, PASS)

print("Logged in as %s (uid: %d)" % (USER, uid))

# Create a new note

sock = xmlrpc.client.ServerProxy(root + 'object')

args = {

'color' : 8,

'memo' : 'This is a note',

'create_uid': uid,

}

note_id = sock.execute(DB, uid, PASS, 'note.note', 'create', args)

练习

在客户端加入新服务

编写一个 Python 程序,可以将 XML-RPC 请求发送给运行 Odoo 服务器。这个程序

应该显示所有的 session 和对应的 seat。它还应该为其中一个 course 创建一个新

session。

import functools

import xmlrpc.client

HOST = 'localhost'

PORT = 8069

DB = 'openacademy'

USER = 'admin'

PASS = 'admin'

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

ROOT = 'http://%s:%d/xmlrpc/' % (HOST,PORT)

# 1. Login

uid = xmlrpc.client.ServerProxy(ROOT + 'common').login(DB,USER,PASS)

print("Logged in as %s (uid:%d)" % (USER,uid))

call = functools.partial(

xmlrpc.client.ServerProxy(ROOT + 'object').execute,

DB, uid, PASS)

# 2. Read the sessions

sessions = call('openacademy.session','search_read', [], ['name','seats'])

for session in sessions:

print("Session %s (%s seats)" % (session['name'], session['seats']))

# 3.create a new session

session_id = call('openacademy.session', 'create', {

'name' : 'My session',

'course_id' : 2,

})

相较于 id,程序也可以通过 name 去找一条记录

# 3.create a new session for the "Functional" course

course_id = call('openacademy.course', 'search', [('name','ilike','Functional')])[0]

session_id = call('openacademy.session', 'create', {

'name' : 'My session',

'course_id' : course_id,

})

2.14.2 JSON-RPC Library

以下的例子使用 Python3 标准的 urllib.request 和 json 类库访问 Odoo 服务器:

import json

import random

import urllib.request

def json_rpc(url, method, params):

data = {

"jsonrpc": "2.0",

"method": method,

"params": params,

"id": random.randint(0, 1000000000),

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

}

req = urllib.request.Request(url=url, data=json.dumps(data).encode(), h

eaders={

"Content-Type":"application/json",

})

reply = json.load(urllib.request.urlopen(req))

if reply.get("error"):

raise Exception(reply["error"])

return reply["result"]

def call(url, service, method, *args):

return json_rpc(url, "call", {"service": service, "method": method, "ar

gs": args})

# log in the given database

url = "http://%s:%s/jsonrpc" % (HOST, PORT)

uid = call(url, "common", "login", DB, USER, PASS)

# create a new note

args = {

'color': 8,

'memo': 'This is another note',

'create_uid': uid,

}

note_id = call(url, "object", "execute", DB, uid, PASS, 'note.note', 'creat

e', args)

XML-RPC 和 JSON-RPC 之间可以实现相互转换。

还有一些高完成度的 API 可以不用显示地通过 XML-RPC 或 JSON-RPC 访问 Odoo,

诸如:

• https://github.com/akretion/ooor

• https://github.com/syleam/openobject-library

• https://github.com/nicolas-van/openerp-client-lib

• http://pythonhosted.org/OdooRPC

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

• https://github.com/abhishek-jaiswal/php-openerp-lib

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

3. ODOO ORM API 接口开发手册

3.1 RECORDSETS

8.0 版本的新特性:本文主要关注 odoo 在 8.0 中新增加的 API——从此可以看出 odoo

下一步的发展方向。此外,也提供了 7.0 之前从各处移植过来的旧 API 的简略信息。如果

需要详细内容,请参考相关文档。

Recordsets 由一若干相同模型的记录组成,是 odoo 中(代码)model 与(数据库)

record 之间的交互方式。

警告

与集合的定义不同的是,理论上记录集合允许重复出现相同记录,这个情况有待日后

修正。

可以在模型中定义方法,这些方法被一个 Recordset 调用,而方法中的参数 self 就是

调用方法的那个对象:

class AModel(models.Model):

_name = 'a.model'

def a_method(self):

self 可以是数据库任意数量的 record 组成的 set

self.do_operation()

程序将在 RecordSet 上迭代,产生 singletons(若干个只包含一个 Record 的 Set),就

像对 Python 的字符串进行迭代一样。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

def do_operation(self):

print self # => a.model(1, 2, 3, 4, 5)

for record in self:

print record # => a.model(1), then a.model(2), then a.model(3), ...

3.1.1 访问字段

Recordsets 提供了一个名为“Active Record”的接口:

其对应的模型中的字段可以直接作为一种属性被程序中的内存对象读取或写入,但这

个过程只能在 singletons 中进行。字段的值也可以通过字典形式被访问,这比 python 中

常用的动态方法 getattr()更加优雅和安全。更新字段的值会触发对数据库的更新:

>>> record.name

Example Name

>>> record.company_id.name

Company Name

>>> record.name = "Bob"

>>> field = "name"

>>> record[field]

Bob

在 RecordSet 上对字段读取或赋值将会引发错误。

访问一个关系字段(Many2one,One2many,Many2many)总是会返回一个 RecordSet,

如果字段没有为空则返回空集合。

注意

对字段的每次赋值都会触发数据库更新;当需要同时对一个或多个对象的字段赋值时,

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

请使用 write()方法。

# 更新 records 的三个字段

for record in records:

record.a = 1

record.b = 2

record.c = 3

# len(records) database updates

for record in records:

record.write({'a': 1, 'b': 2, 'c': 3})

# 1 database update

records.write({'a': 1, 'b': 2, 'c': 3})

3.1.2 记录缓存和预读机制

出于性能方面考虑,Odoo 在缓存中维护了 record 的字段(把值写进缓存),不是每

次对字段的访问都会发出一个数据库请求。下面的示例中只有第一个语句查询了数据库:

record.name # 第一次的字段访问将从数据库读取值

record.name # 第二次的字段访问将从缓存中获取值

为了避免实际生产时每次访问数据库只读取一个字段,Odoo 将(依照以下机制)智

能预读字段,以获得良好的性能。一旦某个字段必须在给定的 Record 上读取,ORM 就会

在一个(后续将用到的)recordset 中访问数据库,并将返回的值存储在缓存中,以便以后

使用。预取的 recordset 通常是在程序中被迭代的 recordset。此外,所有简单的存储字段

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

(boolean、integer、float、char、text、date、datetime、selection、Many2one)都将在这

个过程中被直接获取——通过模型字段与数据库字段之间的一对一关系,被有效预获取。

看一下示例,其中合作伙伴是一个 1000 长度的多例集合。如果不进行预取,循环将向

数据库发出 2000 次访问请求。通过预取机制,只访问一次:

for partner in partners:

print partner.name # 第一次预获取将从数据库中读取 partners

这个多例集合对应的所有记录的 name、lang

# (and other fields) on all 'partners'

print partner.lang

预读机制也适用于次级记录:当读取关系字段时,它们的值(即次级记录)将进行预读。

访问其中一个二级记录将从这个模型中获取所有的简单字段。这使得下面的示例只生成两

个查询,一个用于 partners,一个用于 partners 对应的 country_id 的多例集合:

countries = set()

for partner in partners:

country = partner.country_id # first pass prefetches all

partners

countries.add(country.name) # first pass prefetches all

countries

3.1.3 Recordset 操作

recordset 是不可变的,但是同一模型的多个 recordset 之间允许通过的集合操作,返

回新的 recordset。这种操作不能保持原来的顺序。

记 record 为数据库的每条记录,set 为 recordset,set1 位 recordset1,set2 位

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

recordset2。

•record in set 将返回这个记录(必须为 singleton)是否在对象集合(set)存在;

•record not in set 将执行反向操作。

•set1 <= set2 和 set1 < set2 返回 set1 是否是 set2 的子集

•set1 >= set2 和 set1 > set2 返回 set1 是否为 set2 的超集

•set1 | set2 返回两个 recordset 的并集,这是一个新的 recordset,其中包含两个集合

中不重复的所有记录

•set1 & set2 返回两个 recordset 的交集,这是一个新的 recordset,包含两个源文件中

同时存在的记录

•set1 - set2 返回一个新的 recordset,它包含只存在 set1 而不存在 set2 的记录

3.1.4 其他 Recordset 的操作

recordset 是可迭代的,所以通常的 Python 工具可以用于基本转换(map()、sort()、

itertools。但是无论返回的是列表或者是迭代器,都将无法调用相应的模型方法,或者上述

的 set 操作

因此,如有需要,建议对记录集合使用这些以下操作(这将返回一个记录集合):

filtered()

返回只包含满足断言的 recordset。断言也可以是一个与字段有关的布尔表达式。

records.filtered(lambda r: r.company_id == user.company_id) #只保留那些与当前

用户 company_id 一致的记录

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

records.filtered("partner_id.is_company") #只保留那些

partner_id 字段的 is_company 方法返回 True 的 record

sorted()

返回按提供的键函数排序的 recordset。如果没有提供排序条件,则使用模型的默认

排序顺序:

# 根据 record 的 name 字段对 recordset 重新排序

records.sorted(key=lambda r: r.name)

mapped()

为 recordset 中的所有记录提供一个通用方法,如果方法返回值为一条 record,那么

返回一个由每个 singleton 执行方法获取的 record 组成的 recordset。

# 返回一个列表,由 recordset 中每个记录的字段之和组成

records.mapped(lambda r: r.field1 + r.field2)

所提供的函数可以是获取记录的某个字段的值:

# 返回一个 name 列表

records.mapped('name')

# 返回一个 partner_id 的记录集合

record.mapped('partner_id')

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

# 返回 partner_id 对应的所有不重复的 bank_ids 的记录列表

record.mapped('partner_id.bank_ids')

3.2 ENVIRONMENT

Environment 储存了运行过程中 ORM 使用到的各种上下文数据:数据库游标(用于数据

库查询)、当前用户(用于访问权限检查)和当前上下文(存储任意元数据)。环境同时存储缓

存。

所有 recordset 都有一个环境,它是不可变的,可以使用 env 访问并获取当前用户

(user)、数据库游标(cr)或上下文(context):

>>> records.env

<环境对象...>

>>> records.env.user

res.user(3)

>>> records.env.cr

<数据库游标对象...)

当从其他 recordset 创建新的 recordset 时,environment 也被带过去了。env 常用于创

建其他模型的空 recordset,并查询其中的所有 record:

>>> self.env['res.partner']

res.partner

>>> self.env['res.partner'].search([['is_company', '=', True], ['customer', '=',

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

True]])

res.partner(7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29, 15, 23,

28, 74)

3.2.1 Environment 变更

环境可以在记录集合中自定义。这将返回一个使用更改后的环境的记录集合。

sudo()

使用参数中的用户身份创建一个新环境(用以替代当前环境)。默认为最高管理员身

份,通常用于变更访问权限,通过记录规则的检查等。

# 使用管理员权限创建 partner 对象

env['res.partner'].sudo().create({'name': "A Partner"})

# 使用 public 权限搜索 partner 对象

public = env.ref('base.public_user')

env['res.partner'].sudo(public).search([])

with_context()

1.可以接收一个 context 字典,用以替代当前环境的上下文

2.可以接受一系列键值对形式的参数,用以补充/替代当前上下文中对应的键值对。

# look for partner, or create one with specified timezone if none is

# found

env['res.partner'].with_context(tz=a_tz).find_or_create(email_addr

ess)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

with_env()

用以完整替代当前环境

3.3 通用的 ORM 方法

search()

输入一个搜索域,返回一个匹配的记录集合。可以使用 offset,limit,order 参数。

>>> # 对当前模型进行搜索

>>> self.search([('is_company', '=', True), ('customer', '=',

True)])

res.partner(7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29,

15, 23, 28, 74)

>>> self.search([('is_company', '=', True)], limit=1).name

'Agrolait'

如 果 想 计 算 该 记 录 的 数 量 , 或 者 检 查 是 否 存 在 匹 配 记 录 ( 数 量 为 0) , 请 使 用

search_count()

create()

创建记录:输入若干字段的名称、值,返回一个包含新建记录的单例集合:

>>> self.create({'name': "New Name"})

res.partner(78)

write()

更新记录:输入若干字段的名称、值,将它们更新至记录集合的所有记录中。没有返

回值:

self.write({'name': "Newer Name"})

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

browse()

获取记录:输入一个整数,值为目标记录在数据库中的 id;或者一个整数列表,如果存在

多个目标记录。返回一个记录集合。

常用于:

1)旧 API 调用方法

2)从外部通过 odoo 环境访问数据库

或 ids 列表并返回一个记录集,当记录 id 从外部 Odoo(例如通过外部系统的往返)或在旧

API 中调用方法时有用:

>>> self.browse([7, 18, 12])

res.partner(7, 18, 12)

exists()

检查记录:筛选数据库不存在的记录,返回一个新记录集。常用于检查来自外部 ID 或者权

限受限的情况下记录是否仍然存在:

if not record.exists():

raise Exception("The record has been deleted")

或者在调用删除方法后使用:

records.may_remove_some()

# 只保留没有被删除的记录

records = records.exists()

ref()

输入一个外部 ID,返回与之匹配的数据库记录:

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

>>> env.ref('base.group_public')

res.groups(2)

ensure_one()

检查记录集合是否为单例集合,如果不是将抛出异常

records.ensure_one()

# 实际效果等同于:

assert len(records) == 1, "Expected singleton"

3.4 创建模型

模型的字段,即模型实例的参数:

from odoo import models, fields

class AModel(models.Model):

_name = 'a.model.name'

field1 = fields.Char()

警告

这意味着字段和方法不能重名,它们将产生冲突;

字段的 string 参数(用户可见名称)默认为首字母大写的字段名称,可以用自定义字符串

重写:

field2 = fields.Integer(string="an other field"

对于字段的各种类型和参数,请参阅 the fields reference。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

默认值被定义为字段的参数,值为:

Default 参数用于定义字段的缺省值,可以输入一个定值:

a_field = fields.Char(default="a value")

或者一个计算缺省值的方法,通常需要返回一个表达式:

def compute_default_value(self):

return self.get_value()

a_field = fields.Char(default=compute_default_value)

3.4.1 计算型字段

除了发出数据库请求,字段还可以通过 compute 参数自动计算。这种字段必须分配一个方

法。如果它还依赖了这个模型的其他字段,那么需要使用 depends()来声明那些字段:

from odoo import api

total = fields.Float(compute='_compute_total')

@api.depends('value', 'tax')

def _compute_total(self):

for record in self:

record.total = record.value + record.value * record.tax

•可以依赖次级字段:

@api.depends('line_ids.value')

def _compute_total(self):

for record in self:

record.total = sum(line.value for line in record.line_ids)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

•计算字段默认不储存在数据库,它们是在必要时进行计算和返回的。设置 store =

True 会将它们存储在数据库中,并允许搜索

•还可以通过 search 参数来配置对计算字段的搜索,参数值为一个返回 Domains 的方

法:

upper_name = field.Char(compute='_compute_upper',

search='_search_upper')

def _search_upper(self, operator, value):

if operator == 'like':

operator = 'ilike'

return [('name', operator, value)]

•可以通过 inverse 参数,在计算型字段上手动赋值,来反写它的源计算字段(这个字

段仍然会被重新计算):

document = fields.Char(compute='_get_document',

inverse='_set_document')

def _get_document(self):

for record in self:

with open(record.get_document_path) as f:

record.document = f.read()

def _set_document(self):

for record in self:

if not record.document: continue

with open(record.get_document_path()) as f:

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

f.write(record.document)

•多个字段可以在同一时间以相同的方法计算,只需在所有字段上使用相同的方法,并

设置所有字段:

discount_value = fields.Float(compute='_apply_discount')

total = fields.Float(compute='_apply_discount')

@depends('value', 'discount')

def _apply_discount(self):

for record in self:

# compute actual discount from discount percentage

discount = record.value * record.discount

record.discount_value = discount

record.total = record.value - discount

3.4.2 关联字段

计算字段的一个特殊情况是关联 (代理)字段,其本质为当前记录的一个次级字段。通

过 related 参数进行配置,仍然默认不保存在数据库中:

nickname = fields.Char(related='user_id.partner_id.name',

store=True)

onchang 方法:在页面更新字段

当用户在表单中更改字段的值(但尚未保存表单)时,onchange 方法将根据新值自动更

新其他字段(例如,在更改税收或添加新的发票行时自动更新其他字段。)

•计算字段会自动检查和重新计算,它们不需要被 onchange

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

@api.onchange('field1', 'field2') # 如果 field1,field2 的值在页面上

被改变,调用该方法

def check_change(self):

if self.field1 < self.field2:

self.field3 = True

在方法执行期间的更改将被发送到客户端,并对用户可见;

•计算字段和新 api onchanges 都是由客户端默认自动调用

•可以通过在视图中添加 on_change ="0 "来禁止特定字段的调用:

<field name="name" on_change="0"/>

onchange 方法不会与数据库产生任何交互。

3.4.3 低级的 SQL

Environment 中的 cr 属性是当前数据库事务的游标,它允许直接执行 SQL,无论是出

于 ORM 代码的可读性(过于复杂的关联),还是由于性能原因:

self.env.cr.execute("some_sql", param1, param2, param3)

如上文所述,odoo 将优先从缓存中获取数据;另一方面,直接使用 SQL 语句不会重

置相关缓存。因此,使用 CREATE、UPDATE 或 DELETE 时,必须使用 self.env.invalidate_all()

来清除缓存;但 SELECT 并不影响。

可以通过 Environment 对象的 invalidate_all()清除缓存。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

3.5 旧 API 与新 API 的兼容性

Odoo 目前正在从一个旧(不常用的)API 中过渡,如有必要可以进行手动桥接:

RPC 层(XML-RPC 和 JSON-RPC)都是用旧的 API,新 API 修饰的方法在 RPC 层不可

用。

可重写的方法可以用旧的 API 风格中编写的旧代码中调用。

新旧 api 之间较大的差异是:

Environment 的值(cursor, user_id 和 context)被显式地传递给方法。

记录数据(ids)被显式地传递给方法,可能根本没有被调用

方法倾向于使用 id 列表而不是 recordsets。

默认情况下,方法被假定使用新的 API 样式,并且不能从被旧 API 样式的方法调用。

新旧 API 之间的桥接

从新 API 调旧 API 样式时,将进行自动转换,无需额外操作。

>>> # method in the old API style

>>> def old_method(self, cr, uid, ids, context=None):

... print ids

>>> # method in the new API style

>>> def new_method(self):

... # system automatically infers how to call the old-style

... # method from the new-style method

... self.old_method()

>>> env[model].browse([1, 2, 3, 4]).new_method()

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

[1, 2, 3, 4]

两种修饰风格可以由新风格转为旧风格。

model()

被修饰的方法不会用到 id参数,返回的 recordset通常为空。旧API的签名为cr, uid,

*arguments, context

@api.model

def some_method(self, a_value):

pass

# can be called as

old_style_model.some_method(cr, uid, a_value, context=context)

multi()

用于接受一个由 id 组成的列表,旧 API 的签名为 cr, uid, ids, *arguments, context

@api.multi

def some_method(self, a_value):

pass

# can be called as

old_style_model.some_method(cr, uid, [id1, id2], a_value,

context=context)

因为新 API 视图返回一个 recordset,而旧 API 会返回一个由 id 组成的列表,有一种修

饰符来管理这种情况:

returns()

这个函数用来返回一个 recordset,参数名应为这个 recordset的模型名称,或者 self。

在旧 API 中返回的 recordset 会被拆分成一个由 id 组成的列表。

>>> @api.multi

... @api.returns('self')

... def some_method(self):

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

... return self

>>> new_style_model = env['a.model'].browse(1, 2, 3)

>>> new_style_model.some_method()

a.model(1, 2, 3)

>>> old_style_model = pool['a.model']

>>> old_style_model.some_method(cr, uid, [1, 2, 3], context=context)

[1, 2, 3]

3.6 模型的引用

class odoo.models.Model(pool, cr)

继承 Model 可以使数据持久化,用于在数据库中创建表和字段。

class user(Model):

...

系统稍后将实例化每个数据库中所有已安装的模型对应的类。

3.6.1 模型通用属性

_name

业务对象名称,用点间隔(对应模型的命名空间)

_rec_name

名称的别名字段,由 osv 的 name_get()使用(默认值为‘_name’字段)

_inherit

如果_name 字段为空,则需要指定一个单独的模型用以继承

如果_name 字段不为空,可以指定多个模型用以多继承

详见 继承与扩展.

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

_order

搜索模型的默认排序字段(默认:‘id’)

_auto

是否需要自动创建数据库表(默认为 True)

如果设置为 False,则重写 init()来创建数据库表

_table

Odoo 自动(_auto 为 True)为模型在数据库创建的表名称。

_inherits

一个字典,用于将继承的父类模型和父模型实例的外键字段一一对应:

字典将父业务对象的_name 映射到相应的外键字段的名称。

_inherits = {

'a.model': 'a_field_id',

'b.model': 'b_field_id'

}

基于_inherits 的实现:新模型可以使用父类模型中的所有字段,但不会再数据库中存储

它们:这些值本身只存在于对应的外键字段,即父类实例中。

注意

if the same field is defined on multiple _inherits-ed

_constraints

一个由(constraint_function, message, fields)组成的 Python 约束列表,字段列表

是指示性的。

自从 8.0 版本已弃用:使用 constrains()替代。

_sql_constraints

一个由(name, sql_definition, message)组成的 Sql 约束列表,当创建数据的时候调

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

_parent_store

关联 parent_left 和 parent_right 字段,设置一个嵌套集合,可以在当前模型的记录上

更快地找到对应的父级数据(默认值:False)

3.6.2 增删改查

create(vals) → record

为模型创建一个新记录。

vals 是一个{模型字段的名称:字段的值}组成的字典,可以使用模型的 create()方法创

建新 record。如果需要,odoo 将自动调用模型的 default_get()方法去补充一些必填但

vals 中不存在的字段。

参数

vals (dict) --

以字典形式为 record 的字段赋值:

{'field_name': field_value, ...}

更多细节请参见 write()

返回值

新记录

可能抛出的异常

AccessError(权限异常) --

用户没有创建涉及到的记录的权限;

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

用户试图去绕过权限规则去创建涉及到的对象。

ValidateError(验证异常) --

用户输入的字段名称不存在,字段的值不符合类型要求

UserError(代码异常) --

由缺少退出条件的递归操作产生的无限循环(比如把一个对象设为自己的父级对象)

browse([ids]) → records

在当前环境下,返回一个[ids]列表与数据库中一一对应的一个 recordset

参数可以为空,一个单独的 id,或者一个 id 序列

unlink() 没有返回值

从数据库删除当前 recordset 中的 record

可能抛出的异常

AccessError(权限异常) --

用户没有删除涉及到的记录的权限;

用户试图绕过权限规则去删除涉及到的对象

UserError(代码异常) --

该记录在被其他记录引用,且不允许删除

write(vals)

用 vals 字典参数去更新 recordset 中的所有 record 的字段

参数

vals (dict) --

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

以字典形式出现的字段名称和值,比如:

{'foo': 1, 'bar': "Qux"}

会更新到每个 record 中,如果字段名称、字段的值都有效的话(不然会抛出一个异常)

可能抛出的异常

AccessError(权限异常) --

用户没有修改涉及到的记录的权限

用户试图绕过权限规则去删除涉及到的对象

ValidateError(验证异常) --

用户输入的字段名称不存在,字段的值不符合类型要求

UserError(代码异常) --

由缺少退出条件的递归操作产生的无限循环(比如把一个对象设为自己的父级对象)

对于数字型字段(Integer, Float),输入值应该符合相应的类型(是否存在小数);

对于布尔型字段,输入值应该为 True 或者 False ;

对于选择型字段,输入值应该为预设的选项之一(通常为 str 类,有时为 int 类)

对于 Many2one 型字段,输入的值应为数据库 record 在的数据库标识

其他非关系型字段的值基本为字符串类型

注意

出于一些历史原因和兼容性的考虑,Date 和 Datetime 字段使用字符串而不是

python 中的 date 类和 datetime 类进行读取和写入。统一参照 utc 时间,并按照

odoo.tools.misc.DEFAULT_SERVER_DATE_FORMAT 和

odoo.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT 格式化的

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

One2many 和许多 Many2Many2many many 使用一种 odoo 特有的机制去处理这类字段

相关的 recordset。

以下是一些能被 odoo 识别的特定元组,其中每个元组中的元素都将被顺序执行,用

以执行 record 上的命令。(注:部分指令使用场景可能有所限制)

可能的指令有:

(0, _, values)

使用输入的 values 字典创建新对象

(1, id, values)

使用输入的 values 字典来更新现有的 record。不能被用于 create()方法

(2, id, _)

从 recordset 中删除这个 record 的 id,然后从数据库删除这个 record。不能被用于

create()方法。

(3, id, _)

从 recordset 中删除这个 record 的 id,但不会从数据库删除这个 record。不能被用于

One2many 字段,不能被用于 create()方法。

(4, id, _)

往 recordset 中增加一个已存在的 record 的 id,不能被用于 One2many 字段。

(5, _, _)

从 recordset 中删除所有 record 的 id,但不会从数据库删除这个 record。类似于(4,

id, _)指令,但目标为 recordset 中的所有 record。不能被用于 create()方法。

(6, _, ids)

使用输入的 ids 列表替换现有的所有记录,类似于先用(5, _, _)指令删除,再用(4,

id, _)添加 id

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

元组中被标记为‘_’的部分在程序中作为参数不会被使用,所以理论上可以是任意值,但

建议写 0 或者 False

read([fields])

通常我们可以通过 self、sql 低级查询、RPC 方法来获取特定记录的特地字段。在

Python 代码中,这里更倾向于 browse()方法。

参数

fields:

一个由字段名称组成的列表(默认为一个包含所有字段名称的列表)

返回值

一个由若干字典组成的列表,列表的长度为 recordset 中的 record 数量,字典的内容

为每个 record 与上述的 fields 列表中的每个字段一一对应的键值对。

可能抛出的异常:

AccessError(权限异常):

用户没有获取特定记录的权限

read_group(domain, fields, groupby, offset=0, limit=None, o

rderby=False,lazy=True)

参数

fields (list):

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

用于遍历记录字段的列表,必填

groupby (list):

用于分组的列表,数据将根据这个字段进行分组。group 字段可以是一个单独的字段,

或者一个函数。目前支持的函数有 day', 'week', 'month', 'quarter' or 'year',它们只有对

date/datetime 字典分组时才有意义。

offset (int) :

搜索语句的偏移量,默认为 0。

limit(int) :

搜索语句的数量,默认为 0.

orderby(list) :

排列规则,可以参考 search()重写默认的排序规则(当前只支持 Many2one 字段)

lazy(bool) :

如果为 true,返回值将根据 group_by 中第一个字段进行分组,其余字段存进_context

中;

如果为 false,返回值将根据 group_by 中的搜索字段进行分组

返回值

一个由字典组成的列表,列表的长度由记录的数量决定:

1)通过 groupby 分组的字段字段

2)__domoain:搜索规则的相关列表

3)__context:groupby 用到的参数字典

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

可能抛出的异常:

AccessError(权限异常):

用户没有获取特定记录的权限

用户试图绕过访问规则读取特定记录

搜索

search(args[, offset=0][, limit=None][, order=None][, count=False])

搜索满足条件的 records

参数

args:

搜索域,[(字段名称,运算符,值),(…),],必填;

用于筛选 record 的条件列表;如果为空,则将匹配所有 record。

offset:

偏移量,Integer 类,选填,默认效果:从 0 开始获取 record;

用于控制忽略(不返回)前 offset 条 record。

limit:

数量,Integer 类,选填,默认效果:获取所有 record;

用于控制返回的 recordset 中 record 的数量。

order:

排序字段,String 类,选填,默认效果:参照相关模型的排序规则进行排

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

序;

用于控制返回的 recordset 中的排序规则,根据该字段进行升序排列。

count:

是否计数,Boolean 字段,选填,默认效果:返回通过筛选的 recordset;

如果为 True,不再返回 recrodset,而返回 recordset 中 record 的数量。

返回值

如果 count 为 False,返回符合其他条件的 recordset;

如果 count 为 True,返回返回其他条件的 recordset 中 record 的数量。

可能抛出的异常:

AccessError(权限异常):

用户试图去绕过权限规则去读取指定对象的数据

search_count(args) → int

返回匹配搜索域的 recordset 中 record 的数量

name_search(name='', args=None, operator='ilike', limit=100) → records

根据 display_name、及其他字段进行筛选,返回符合条件的 record 的 id 和

display_name

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

通常使用这个方法,根据关联字段拼接新的展示名称。有时它被看成 name_get()的

反函数,但未必如此。

如果对 display_name 根据指定的搜索域调用 search()方法,这个方法等同于

name_get()

参数

name :

名称,String 类,必填;

与 operator 联用,用于筛选 record 的 display_name 字段。

args :

搜索域,[(字段名称,运算符,值),(…),],选填,默认效果:匹配所有 record;

用于筛选 record 的条件列表;如果为空,则将匹配所有 record。

operator:

运算符,String 类,必填;

与 name 联用,用于筛选 record 的 display_name 字段;

通常使用‘'like' ’, '='。

limit:

数量,Integer 类,选填,默认效果:获取所有 record;

用于控制返回的 recordset 中 record 的数量。

返回值

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

返回一个由元组组成的列表,元祖由每个符合条件的 id 和 display_name 组成。

3.6.3 RecordSet 运算

ids

由 recordset 中每个 record 的 id 组成的列表

ensure_one()

验证当前的 recordset 是否只包含一个 record;不然就抛出异常。

exists() → records

筛选 recordset 中无法访问的 record(权限限制,已删除等),返回筛选后的

recordset。常用于日常检查:

if record.exists():

...

filtered(func)

筛选 recordset 中不符合函数要求的 record,返回筛选后的 recordset。

参数

fun -- a function or a dot-separated sequence of field names

sorted(key=None, reverse=False)

返回重新排序的 recordset

参数

key:

排序规则,String 类,选填;

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

用来控制每个 record 的排序值,并影响之后的排列顺序。

取值可以是一个模型字段,也可以是一个返回值可以相互比较的自定义函数的名称。

如果为空,参照相关模型的排序规则进行排序。

reverse :

是否倒序,Boolean 类,选填,默认效果:升序;

如果为 True,则 recordset 将根据 key 倒序排列。

mapped(func)

使 recordset 中的所有 record 都调用 func 函数,并将对应的返回值放置一个列表中

统一返回;如果返回的是 recordset,那么新 recordset 将依照原 recordset 的顺序,而是

参照 id 升序排列

参数

func——用点进行分隔的字段名(字符串)序列;任何错误的值都会返回记录集 self。

3.6.4 环境转换

sudo([user=SUPERUSER])

返回一个具有新身份(新 user)下的 recordset。

默认情况下,返回最高管理员下的 recordset,操作时不会受到任何限制。

使用 sudo()将允许程序中的数据自由通过任何限制规则,可能导致本应相互隔离

的记录混在一起(比如在一个多公司的环境中)

有时它产生的影响是不确定的,常见于一个字段多选一的默认取值——例如,获

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

得默认的公司,或者选择一份材料清单。

因为使用 sudo()将产生的记录规则和访问权限,所以新 recordset 不能调用当前

的环境缓存,所以稍后的数据访问会先重新从数据库获取相应环境数据,导致产生额

外的延迟。返回的 recordset 因为环境的传递性因此和 self 一样将获取同样的预取对

象。

with_context([context][, **overrides]) → records

返回一个具有被扩展的上下文的 recordset

overrides 中的键值对将逐一更新传进来的 context,或者当前的上下文,用来形成新

的上下文。

# 当前上下文是 {'key1': True}

r2 = records.with_context({}, key2=True)

# -> r2 的上下文是 {'key2': True}

r2 = records.with_context(key2=True)

# -> r2 的上下文是 {'key1': True, 'key2': True}

with_env(env)

返回一个具有新环境的上下文的 recordset

因为使用 sudo()将产生的记录规则和访问权限

Warning

新 recordset 不能调用当前的环境缓存,所以稍后的数据访问会先重新从数据库获

取相应环境数据,导致产生额外的延迟。返回的 recordset 因为环境的传递性因此和

self 一样将获取同样的预取对象。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

3.6.5 检索字段、视图

fields_get([fields][, attributes])

返回字段的参数字典,包括通过_inherits 方式继承的字段。

参数

fields :

字段名称,String 类/[字段名称,…],选填,默认效果:一个由该模型的所有字段组成

的列表;

attributes:

参数名称,String 类/[参数名称,…],选填,默认效果:一个由该字段的所有参数组成

的列表;

fields_view_get([view_id | view_type='form' [,toolbar=False [,submenu

=False]]])

获取视图的细节组成部分,比如字段、模型、视图结构

参数

view_id:

视图 id,Integer 类,选填,默认效果:获取该模型的 view_type 类型的第一

个视图;

view_type:

视图类型,String 类,选填,默认效果:如果 view_id 为空,默认为 form

类型;

toolbar :

是否包含上下文,Boolean 类,选填,默认效果:不包含;

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

submenu :

已弃用

返回值

返回视图的参数字典,包括已继承和被继承的部分。

可能抛出的异常

AtributeError(参数异常):

继承视图时,position 参数的值被错写成'before', 'after', 'inside', 'replace'

之外的值;

出现了 position 意外的其他 tag 标签

Invalid ArchitectureError(无效的构建异常):

视图类型的值被错写为‘form’, ‘tree’, ‘calendar’, ‘search’等

预设值之外的其他值

3.6.6 其他方法

default_get(fields) → default_values

返回 fields 中所有字段对应的默认值。默认值通常由上下文,用户自定义默认值,及

模型本身决定。

参数

fields:

字段名称,[字段名称,…],必填;

返回值

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

字段名称与默认值一一对应的一个字典、

返回值

a dictionary mapping each field name to its corresponding default value, if it

has one.

copy(default=None)

创建一个 record 的副本,并用 deault 更新这个副本

参数

default:

值字典,[字段名称:字段的值],选填,默认效果:不更新副本;

用以重写副本中指定字段的值。

返回值

一个新的 record(原 record 的副本)

name_get() → [(id, name), ...]

返回 recordset 里每个 record 的 id 和 display_name

返回值

一个由(record 的 id,record 的 display_name)组成的列表

name_create(name) → record

调用 create({‘display_name’:name}),返回新 record 的 name_get()。

这要求模型中的必填字段可以从上下文中初始化。

参数

name :

字段名称,String 类,必填;

display_name 字段的值。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

返回值

新 record 的 name_get()返回值

3.6.7 预设字段

id

表示字段,Integer 类。

_log_access

相关的 log access fields(例如:create_date, write_uid, ...)是否被自动创建,

Boolean 类,默认为 True。

create_date

创建 record 的时间,Datetime 类。

create_uid

创建 record 的用户,res.users 类。

write_date

最后一次更新 record 的时间,Datetime 类

write_uid

最后一次更新 record 的用户,res.users 类。

3.6.8 常见字段

除了预设字段之外,这里还提供了一些常见字段以供参考:

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

name

_rec_name 的初始值,用于展示 record 的名称,Char 类。

active

用于控制 record是否对用户可见。如果为 False,那么 record对用户的大多数批处理方法不可见,

Boolean 类。

sequence

可更改的排序标识,允许在列表视图中进行拖放实现重新排序,Integer 类。

state

用于标识 record 的生命周期,Selection 类。

parent_id

用于实现树状结构,同时允许在搜索域中执行 child_of 运算符。

parent_id

用于_parent_store,加速树状结构的随机访问。

parent_right

详见 parent_left

3.7 方法修饰符

这个模块提供了两种不同的 API 样式,即“传统”和“记录”样式。

在“传统”样式中,诸如数据库游标、用户 id、上下文字典和记录 id(通常表示为 cr、

uid、context、id)的参数被显式地传递给所有方法;在“record”样式中,这些参数被隐藏到

模型实例中,这给人一种更面向对象的感觉。

例如,以下语句:

model = self.pool.get(MODEL)

ids = model.search(cr, uid, DOMAIN, context=context)

for rec in model.browse(cr, uid, ids, context=context):

print rec.name

model.write(cr, uid, ids, VALUES, context=context)

也可以被写为:

env = Environment(cr, uid, context) # cr, uid, context wrapped in env

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

model = env[MODEL] # retrieve an instance of MODEL

recs = model.search(DOMAIN) # search returns a recordset

for rec in recs: # iterate over the records

print rec.name

recs.write(VALUES) # update all records in recs

odoo 将根据“传统”样式的方法中的参数,对进行自动修饰。

odoo.api.multi(method)

修饰一个“record”样式的方法,self 即 record 本身。该方法通常对 record 进行操作。

例如:

@api.multi

def method(self, args):

...

“传统”和“record”样式:

# recs = model.browse(cr, uid, ids, context)

recs.method(args)

model.method(cr, uid, ids, args, context=context)

odoo.api.model(method)

修饰一个“record”样式的方法,self 即 recordset。方法中的实际业务并不涉及 recordset

中的具体实例,只依赖模型本身(类似于静态方法)。例如:

@api.model

def method(self, args):

...

“传统”和“record”样式:

# recs = model.browse(cr, uid, ids, context)

recs.method(args)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

model.method(cr, uid, args, context=context)

注意,“传统”样式中不传 ids 参数(因为不依赖具体实例)。

odoo.api.depends(*args)

适用于计算型字段调用的 compute 方法。其中的每个参数以逗号分隔,必须是该模型

已定义的字段名称:

pname = fields.Char(compute='_compute_pname')

@api.one

@api.depends('partner_id.name', 'partner_id.is_company')

def _compute_pname(self):

if self.partner_id.is_company:

self.pname = (self.partner_id.name or "").upper()

else:

self.pname = self.partner_id.name

也可以通过一个函数作为参数。在这种情况下,依赖关系将由这个函数决定。

odoo.api.constrains(*args)

约束修饰符,其中的声明字段必须是有待检查的字段名称:

@api.one

@api.constrains('name', 'description')

def _check_description(self):

if self.name == self.description:

raise ValidationError("Fields name and description must be

different")

当字段的值被更新时调用该方法,如果验证失败将抛出 ValidationError。

注意

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

@constrains 只支持简单的字段名,次级字段(关系字段的字段,例如

partner_id.customer)不受支持,并且将被忽略。

在 create(values)或 wirte(values)方法执行时,如果 values 存在修饰方法

的声明字段时,才会调用这个修饰方法。这意味着,如果视图中不存在一个字段,

在创建或更新时 odoo 不会通过@constrains 做类似的非空校验。因此,如有必

要可以重写 create()、write()方法。

odoo.api.onchange(*args)

同步变更修饰符,其中的声明字段必须是方法业务依赖的字段:

@api.onchange('partner_id')

def _onchange_partner(self):

self.message = "Dear %s" % (self.partner_id.name or "")

任意声明字段在 form 视图被修改时被伪记录(pseudo-record)(只存在内存中的当前记

录)调用,更新后的字段将(不经过数据库)自动发送回客户端。

该方法可以返回一个字段字典,并弹出一个警告消息,比如在旧的 API 中:

return {

'domain': {'other_id': [('partner_id', '=', partner_id)]},

'warning': {'title': "Warning", 'message': "What is this?"},

}

注意

@onchange 只 支 持 简 单 的 字 段 名 , 虚 名 ( 关 系 字 段 的 字 段 , 例 如

partner_id.tz)不受支持,并且将被忽略。

odoo.api.returns(model, downgrade=None, upgrade=None)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

记录修饰符,返回指定模型的 recordset。

参数

model :

模型名称,String 类,必填;

用以指定方法返回值的类型(但系统不会辅助检查)。

downgrade :

downgrade(self, value, *args, **kwargs)函数,选填;

把“record”样式的值字典转换成“传统”样式的参数

upgrade :

upgrade(self, value, *args, **kwargs)函数,选填;

把“传统”样式的值字典转换成“record”样式的参数

返回值

由修饰方法的返回值决定

参数(self, *args 和**kwargs)是指“record”样式的参数。

修饰符将自动适应方法的返回值,并将其转换至相应样式:

@model

@returns('res.partner')

def find_partner(self, arg):

... # return some record

# output depends on call style: traditional vs record style

partner_id = model.find_partner(cr, uid, arg, context=context)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

# recs = model.browse(cr, uid, ids, context)

partner_record = recs.find_partner(arg)

注意,装饰方法的返回值“需要”符合它指定的模型。

这 些 decorator 是 自 动 继 承 的 : 重 写 修 饰 的 现 有 方 法 的 方 法 将 使 用 相 同 的

@returns(model)进行修饰。

odoo.api.one(method)

修饰一个“record”样式的方法,其中 self 应该是一个 record(而不是 recordset)。被

修饰的方法会自动循环 recordset,并把结果以列表形式返回。如果方法还用 return()修饰,

它会强制要求返回当前 model 的 recordset。

@api.one

def method(self, args):

return self.name

“传统”和“record”样式:

# recs = model.browse(cr, uid, ids, context)

names = recs.method(args)

names = model.method(cr, uid, ids, args, context=context)

自 9.0 版本以来被弃用: one()常常使代码不那么清晰,并且实际运行结果常与开发人

员和读者所设想的有所出入。

在这里强烈建议使用 multi(),在 recordset 上迭代 record,或者确保 recordset 通过

ensure_one() 的检查。

odoo.api.v7(method_v7)

仅使用支持旧式 api 的方法。可以使用 v8()并重写该方法来调用新 api。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

@api.v7

def foo(self, cr, uid, ids, context=None):

...

@api.v8

def foo(self):

...

如果一个方法调用另一个方法,则必须特别小心,因为方法可能被重写!在这种情况下,

应该从当前类(例如 MyClass)调用该方法,例如:

@api.v7

def foo(self, cr, uid, ids, context=None):

# Beware: records.foo() may call an overriding of foo()

records = self.browse(cr, uid, ids, context)

return MyClass.foo(records)

Note that the wrapper method uses the docstring of the first method.

odoo.api.v8(method_v8)

仅对支持新型 api 的方法进行修饰。可以使用 v7()并重写该方法来调用旧 api:

@api.v8

def foo(self):

...

@api.v7

def foo(self, cr, uid, ids, context=None):

...

Note that the wrapper method uses the docstring of the first method.

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

3.8 字段

3.8.1 基础字段

class odoo.fields.Field(string=<object object>, **kwargs)

字段描述符包含字段定义,并管理记录上对应字段的访问和赋值。可以在实例化字段

时定义以下属性:

参数

string:

展示名称,String 类,选填,默认效果:首字母大写的字段名称;

用户在前端看到的字段的标签。

help :

帮助提示,String 类,选填,默认效果:空;

用户悬浮字段时看到的提示语句。

readonly :

是否只读,Boolean 类,选填,默认效果:否;

字段在前端是否只读。

required :

是否必填,Boolean 类,选填,默认效果:否;

字段对 record 而言是否必填。

index :

是否索引,Boolean 类,选填,默认效果:否;

字段在数据库是否创建索引。

default :

默认值,自适应,选填,默认效果:None;

可以是一个定值,也可以是一个以 recordset 为参数的函数。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

states :

默认值,自适应,选填,默认效果:None;

可以是一个定值,也可以是一个以 recordset 为参数的函数。

default :

默认值,自适应,选填,默认效果:None;

可以是一个定值,也可以是一个以 recordset 为参数的函数。

states:

状态 UI 字典,{state:[(UIfield,False)]},选填,默认效果:无;

一个将字段的 UI 属性映射到不同 state 的字典;可能的属性是:'readonly', 'required',

'invisible'。注意:state 键必须在模型的字段中定义过。

This is typically done by including it in the relevant views, possibly made invisible if not

relevant for the end-user.

groups :

权限组,[String 类,…],选填,默认效果:无;

只有权限组里的用户才有字段的访问权限。

copy :

是否同步,Boolean 类,选填;

当调用 record 的 copy()方法时,字段值是否应该被同步至副本。

对于 one2many 和计算字段是 False,including property fields and related fields;

对于其他字段,默认为 True

oldname :

曾用名,String 类,选填,默认效果:无;

该字段的曾用名称,以便 ORM 可以在迁移时自动重命名它。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

3.8.1.1 计算型字段

可以定义一些经过程序计算,而不是从数据库读取的字段。计算型字段的关键

属性如下所示。

参数

compute :

计算字段需要调用的函数名称,必填。

inverse :

计算字段之前调用的函数名称,选填。

search :

重写根据该字段搜索记录的规则的函数名称,选填。

store :

字段是否在数据库中保存,默认为 False。

compute_sudo:

是否以超级管理员权限执行计算方法,默认为 False。

upper = fields.Char(compute='_compute_upper',

inverse='_inverse_upper',

search='_search_upper')

@api.depends('name')

def _compute_upper(self):

for rec in self:

rec.upper = rec.name.upper() if rec.name else False

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

def _inverse_upper(self):

for rec in self:

rec.name = rec.upper.lower() if rec.upper else False

def _search_upper(self, operator, value):

if operator == 'like':

operator = 'ilike'

return [('name', operator, value)]

compute 方 法 必 须 为 recordset 中 的 所 有 record 的 字 段 赋 值 。 可 以 通 过

odoo.api.depends()确定计算方法所需要依赖的字段,这些字段决定了什么时候重新调用

计算方法。重新计算的过程是自动实现的,且会同步至缓存/数据库。注意,一个方法可以

为若干个字段赋值。

默认情况下,计算字段不会存储到数据库中,并且是动态计算的。添加属性

store=True 将在数据库中存储字段的值。存储在数据库的优点是,基于这个字段的搜索是

由数据库本身完成的;缺点是当字段必须重新计算时,它需要与数据库产生交互。

inverse 方法,顾名思义,就是计算方法的反向方法:当用户给计算型字段手动赋值时,

你可能需要使用 inverse 方法,在 compute 方法执行之前先实现一些业务逻辑。注意,没

有 inverse 方法的计算字段在默认情况下是只读的。

在对模型进行实际搜索之前,在处理域时调用 search 方法。它必须返回一个与初始搜

索条件等价的域:(field operator value)。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

3.8.1.2 关联字段

关联字段的值是经过一个字段序列,在最终的字段模型上找到的一个值。这个遍历字

段的过程由关联字段的属性决定。

参数

reltad:

字段名称的序列

如 果 没 有 重 新 定 义 , 关 联 字 段 的 属 性 会 参 考 源 字 段 , 常 见 的 有 :

string, help, readonly, required ; 同 时 也 包

括 : groups, digits, size, translate, sanitize, selection, comodel_name, domain, co

ntext。

默认情况下,相关字段的值不会存储到数据库中。如有需要可以添加属性 Store=True,

就像计算性字段一样。当它们的源字段被修改时,相关字段会自动重新计算。

3.8.1.3 公司依赖型字段

这些字段的值由当前公司决定。换句话说,属于不同公司的用户可能会看到不同的值。

参数

company_dependent:

字段是否会根据公司的不同而变化,Boolean 类。

3.8.1.4 增量定义

odoo 中的字段是作为 class 中的一个属性展开的。如果模型被继承(参见 Model),还可以

在子类中定义相同名称和类型的字段来扩展。在这种情况下,字段的属性将从先父类中获取,

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

再被子类中重新定义属性重写。

例如,下方的 Second 类为 foo 模型中的 state 增加一个提示语属性:

class First(models.Model):

_name = 'foo'

state = fields.Selection([...], required=True)

class Second(models.Model):

_inherit = 'foo'

state = fields.Selection(help="Blah blah blah")

class odoo.fields.Char(string=<object object>, **kwargs)

基于: odoo.fields._String

基础的 String 字段,可以控制字符串长度,通常在客户端以一行文本框展示。

参数

size :

字段能储存的最大字符串的长度。

translate :

启用该字段值的翻译;使用 translate=True 将字段值转换为一个整体; translate 也可

以通过调用 translate(callback, value)把 value 使用 callback(term)获取翻译文本。

class odoo.fields.Boolean(string=<object object>, **kwargs)

基于: odoo.fields.Field

class odoo.fields.Integer(string=<object object>, **kwargs)

基于: odoo.fields.Field

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

class odoo.fields.Float(string=<object object>, digits=<object

object>, **kwargs)

基于: odoo.fields.Field

小数精度由参数决定。

参数

digits :

一个由(总计,小数部分)组成的元组,或者一个由有效返回值的函数。

class odoo.fields.Text(string=<object object>, **kwargs)

Bases: odoo.fields._String

类似 Char 型,但时没有长度限制并会在多行文本框中显示。

参数

translate :

启用该字段值的翻译;使用 translate=True 将字段值转换为一个整体; translate 也可

以通过调用 translate(callback, value)把 value 使用 callback(term)获取翻译文本。

class odoo.fields.Selection(selection=<object object>, string=<object

object>,**kwargs)

基于: odoo.fields.Field

参数

selection :

定义了这个字段的有效值区间,可以是一个由(value, string)组成的元组,也可以是

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

模型的方法名称。

selection_add:

当继承这个字段时,通过这个参数补充字段的有效值区间。

如果这个字段不是关联字段或者继承字段,那么 selection 是必填的。

class odoo.fields.Html(string=<object object>, **kwargs)

基于: odoo.fields._String

class odoo.fields.Date(string=<object object>, **kwargs)

基于: odoo.fields.Field

static context_today(record, timestamp=None)

使用相应格式返回服务器时区的当前日期,此方法可用于计算默认值。

参数

timestamp :

如果不为空,返回这个 datetime 对应的日期。

返回值

相应格式的日期字符串

static from_string(value)

把 value 转换成 date 类型

static to_string(value)

把 date 类型转换成 String 类型

static today(*args)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

使用相应格式返回服务器时区的当前日期,此方法可用于计算默认值。

class odoo.fields.Datetime(string=<object object>, **kwargs)

基于: odoo.fields.Field

static context_timestamp(record, timestamp)

使用相应格式返回服务器时区的当前日期,但这个方法不是用来初始化的,因为

datetime 字段在客户端显示时自动转换。对于默认化字段,建议使用 datetime.now()。

参数

timestamp :

被转换成服务器时间的时间戳。

返回值

按上下文中的时区转换的时间。

static from_string(value)

把 value 转换成 date 类型

static now(*args)

使用相应格式返回服务器时区的当前时间,该方法可用于计算默认值。

static to_string(value)

把 date 类型转换成 String 类型

3.8.2 关联字段

class odoo.fields.Many2one(comodel_name=<object object>, string=<object

object>,**kwargs)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

基于: odoo.fields._Relational

该字段的值是一个长度为 0(即没有 record)或者长度为 1(单例对象)的 recordset

参数

comodel_name :

目标模型的名称

domain :

搜索域,用于控制用户界面中该字段备选值的范围,可选。

context :

上下文,用于当用户调用该字段时提供上下文。

ondelete :

同步删除,当引用的 record 被删除时执行的操作。

可用值:'set null', 'restrict', 'cascade'

auto_join:

whether JOINs are generated upon search through that field (boolean, by

default False)

delegate :

set it to True to make fields of the target model accessible from the current

model (corresponds to _inherits)

class odoo.fields.One2many(comodel_name=<object

object>, inverse_name=<object object>, string=<object object>, **kwargs)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

基于: odoo.fields._RelationalMulti

comodel_name 这个模型中 inverse_name 的值为当前记录的所有记录

参数

comodel_name :

目标模型的名称。

inverse_name :

当前模型在目标模型中对应的 Many2one 字段的名称。

domain :

搜索域,用于控制用户界面中该字段备选值的范围,可选。

context :

上下文,用于当用户调用该字段时提供上下文。

ondelete :

同步删除,当引用的 record 被删除时执行的操作。

可用值:'set null', 'restrict', 'cascade'

auto_join:

whether JOINs are generated upon search through that field (boolean, by

default False)

limit:

控制用户界面的 record 数量

除了关联字段和继承字段,comodel_name 和 inverse_name 是必填项。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

class odoo.fields.Many2many(comodel_name=<object

object>, relation=<object object>,column1=<object object>, column2=<object

object>, string=<object object>, **kwargs)

基于: odoo.fields._RelationalMulti

Many2many 字段; 这些字段的值是一个 recordset

参数

comodel_name :

目标模型的名称。

除了关联字段和继承字段,comodel_name 是必填项。

relation :

数据库的表名称。

column1 :

本模型在数据库表中的字段名称。

column1 :

目标模型在数据库表中的字段名称。

relation, column1 和 column2 是选填项。如果为空,odoo 将自动产生相应的名

称。

domain :

搜索域,用于控制用户界面中该字段备选值的范围,可选。

context :

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

上下文,用于当用户调用该字段时提供上下文。

ondelete :

同步删除,当引用的 record 被删除时执行的操作。

可用值:'set null', 'restrict', 'cascade'

auto_join:

whether JOINs are generated upon search through that field (boolean, by

default False)

limit:

控制用户界面的 record 数量

class odoo.fields.Reference(selection=<object object>, string=<object

object>,**kwargs)

基于: odoo.fields.Selection

3.9 继承与扩展

Odoo 提供了三种不同的机制模块化地扩展模型:

•从现有的模型中创建一个新模型,向新模型中添加新信息,但源模型保持原样。

•对现有的模型进行扩展,取代以前的版本。

•代理模型来记录它相关的内容。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

3.9.1 继承类

当使用_inherit 和_name 属性时,Odoo 根据现有的模型(_inherit),创建

一个新模型(_name)。新模型将自动从源模型获取所有字段、方法和相关信息。

class Inheritance0(models.Model):

_name = 'inheritance.0'

name = fields.Char()

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

def call(self):

return self.check("model 0")

def check(self, s):

return "This is {} record {}".format(s, self.name)

class Inheritance1(models.Model):

_name = 'inheritance.1'

_inherit = 'inheritance.0'

def call(self):

return self.check("model 1")

现在调用各自的 create()方法:

a = env['inheritance.0'].create({'name': 'A'})

b = env['inheritance.1'].create({'name': 'B'})

a.call()

b.call()

将是两个不同模型的记录:

"This is model 0 record A"

"This is model 1 record B"

第二个模型已经继承了第一个模型的 check方法和 name 字段,但重写了 call 方法。

详见 python 的继承机制。

3.9.2 扩展类

当只使用_inherit 但_name 为空时,新模型将取代原来的模型,通过扩展源

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

模型的方式。这有助于向现有模型添加新的字段或方法,或做一些自定义的配置

(例如更改它们的默认排序规则):

class Extension0(models.Model):

_name = 'extension.0'

name = fields.Char(default="A")

class Extension1(models.Model):

_inherit = 'extension.0'

description = fields.Char(default="Extended")

env = self.env

{'name': "A", 'description': "Extended"}

3.9.3 代理类

第三种继承机制提供了更大的灵活性(运行时动态变化),尽管功能有限:使用_inherits

模型将当前模型中没找到的任何字段的查找委托给子模型去找。代理是通过在父模型上的

引用字段来实现的:

class Child0(models.Model):

_name = 'delegation.child0'

field_0 = fields.Integer()

class Child1(models.Model):

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

_name = 'delegation.child1'

field_1 = fields.Integer()

class Delegating(models.Model):

_name = 'delegation.parent'

_inherits = {

'delegation.child0': 'child0_id',

'delegation.child1': 'child1_id',

}

child0_id = fields.Many2one('delegation.child0', required=True,

ondelete='cascade')

child1_id = fields.Many2one('delegation.child1', required=True,

ondelete='cascade')

record = env['delegation.parent'].create({

'child0_id': env['delegation.child0'].create({'field_0':

0}).id,

'child1_id': env['delegation.child1'].create({'field_1':

1}).id,

})

record.field_0

record.field_1

将返回:

0

1

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

同时我们也可以直接去写入字段:

record.write({'field_1': 4})

Warning

当时用代理继承时,只继承字段,不继承方法。

3.10 搜索域

搜索域是一个范围列表,每个范围都是一个(field_name, operator, value) 组成

的 list 或 tuple。

参数

field_name (str)

当前模型的一个字段名称,或者由关系型字段关联的次级字段,

如:'street' or 'partner_id.country'

operator (str)

用来比较 field_name 和 value 的运算符。如下所示:

= 等于

!= 不等于

> 大于

>= 大于等于

< 小于

<= 小于等于

=? 等于 或本身为空

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

=like 是否匹配。

使用_表示任意一个字符,%表示任意多个字符

like 是否匹配 %value%

not like like 的非运算

ilike 不区分大小写的 like

not ilike 不区分大小写的 not like

=ilike 不区分大小写的 ilike

in 是否等于 value 中的任意元素,value 必须是一

个列表

not in in 的非运算

child_of 是否存在父子或父子以上关系

Takes the semantics of the model into account (i.e following the relationship field

named by _parent_name).

value

变量的类型必须能通过 operator 和 field_name 的值进行比较

搜索域的子范围可以用逻辑运算符进行串联:

'&'

逻辑运算符 AND,默认运算符,影响后续的两个范围.

'|'

逻辑运算符 OR, 影响后续的两个范围

'!'

逻辑运算符 NOT, 影响后续的一个范围

写在运算符中的!通常比写在外部的!的可读性更强。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

Example

搜索 名字是’ABC’,来自‘belgium’或‘germany’,语言不是‘english’

的 partner:

[('name','=','ABC'),

('language.code','!=','en_US'),

'|',('country_id.code','=','be'),

('country_id.code','=','de')]

这个搜索域将被编译为:

(name is 'ABC')

AND (language is NOT english)

AND (country is Belgium OR Germany)

3.11 从旧 API 转换到新 API

在新的 API 中应该避免暴露 id 列表,用 recordset 替代。

如果旧 API 方法被新 API 方法调用时,旧 API 方法会被自动转化为新 API 风格,更多

细节请查看 Automatic bridging of old API methods 一章。

search()返回一个 recordset,没有指向性,例如浏览它的结果。

fields.related and fields.function 被 related=和 compute=参数替代。

depends() on compute=必须将实际方法中用到的所有字段及次级字段列举到它的参

数中。更推荐列举所有可能用到的字段(尽管有时根本用不到),而不是少量的一些关键

字段(以免在实际方法中忘了其他的关联字段)

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

删除计算型字段上的所有 onchange 方法。当该字段的依赖字段被更改时,计算型字段

会自动重新计算,并用于自动生成客户端更改。

model()和 multi()修饰是用于从旧 API 上下文调用时桥接的,对于内部方法或新

API(例如 compute)方法来说,它们是无用的。

_default 被 default=参数替代。

如果字段的 String 参数的值只是大写的字段名称,例如:

• name = fields.Char(string="Name")

这种设置是没有意义且应该删除的

multi=参数在新的 API 字段上没有意义,可以使用 compute=参数在所有相关的字段

上执行相同的结果。

对外提供 compute=、inverse=和 search=参数,这使得它们对应的业务逻辑可以被

自定义了。

需要重复检查所有字段和方法都有不同的名称,在发生冲突之前 odoo 不会发出警告

(因为 Python 在 Odoo 框架之前提前处理了这部分)

一般的新 api 可以用 from odoo import fields, models 导入。如果需要处理兼容性

修饰符,使用 from odoo import api, fields, models

避免使用 one()修饰符,它可能不符合您的期望。

删除 create_uid, create_date, write_uid and write_date 的显式定义:

它们现在被当作预设字段创建,并且可以像任何其他字段一样被读取和写入。

如果不可能进行直接转换(语义不能桥接)或“旧 API”版本不可用,同时新 API 可以进

行改进,那么可以使用 v7()和 v8().实现相应需求。该方法首先应该使用老 api 的样式来定

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

义,并用v7()来修饰,然后使用相同的名称重新定义它,但是使用新的api样式并使用v8()

来修饰。来自旧 api 上下文的调用将被发送到第一个实现,并且将向第二个实现发送新 api

上下文的调用。一个实现可以通过切换上下文调用(并经常调用)另一个实现。方法应该首

先被用旧 API 风格定义,被 v7()修饰,然后被新 API 风格重定义,被 v8()重修饰。用旧

API 上下文的调用会被分配到第一种实现,而用新 API 上下文的调用会被分配到第二种实

现。两种实现可以通过上下文互相切换。

危险

使用这些修饰符使得方法非常难以覆盖,而且更难理解和文档化。

注意

使用这些修饰符将使得方法极其难以重写,且可读性极差此外不容易文档化。

_columns 或_all_columns 应该被_fields 参数替换,可以借此访问新样式的

odoo.fields 的实例(而不是老式的 odoo.osv.fields._column)。

用新 API 创建的 store=False 的计算型字段,在_columns 参数中不可用,且只

能通过_fields 进行检查

在方法中不要对 self 重新复制,可能会破坏 odoo 的编译检查。

环境对象依赖于一些本地线程的状态,在使用它们之前必须设置它。当试图使用新

API 还未设置的上下文时,有必要使用 odoo.api.Environment.manage()上下文管理器,这

样做是为了避免新进程或 Python 交

互式环境可能引起的冲突:

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

1. >>> from odoo import api, modules

2. >>> r = modules.registry.RegistryManager.get('test')

3. >>> cr = r.cursor()

4. >>> env = api.Environment(cr, 1, {})

5. Traceback (most recent call last):

6. ...

7. AttributeError: environments

8. >>> with api.Environment.manage():

9. ... env = api.Environment(cr, 1, {})

10. ... print env['res.partner'].browse(1)

11. ...

12. res.partner(1,)

3.11.1 对旧 API 方法的自动桥接

当模型被初始化时,所有的方法都会被自动扫描,如果它们看起来像旧的 API 样式中

声明的模型,那么就会被桥接。这种桥接使它们可以透明地被新 api 样式的方法调用

如果方法的第二个(在 self 之后)参数是 cr 或者 cursor,它将被匹配为“老 api 样

式”。该系统将把第三个参数识别为 uid 或 user,第四个参数识别为 id 或 ids。它还会把

其他参数识别为 context。

当从一个新 API 上下文调用这些方法时,系统将自动从当前环境(对于 cr、user 和

context 来说)或当前 recordset(对 id 和 ids 来说)填充匹配的参数。

对于偶然场景,在必要的情况下,可以通过装饰旧 API 的方法来定制桥接:

通过用 noguess()来装饰来完全禁用这个方法,这样将不会有桥接,此外方法被对应

的风格被调用。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

显示定义桥接,这主要是针对无法被正确匹配的方法(因为参数以意外的方式命名):

cr()

will automatically prepend the current cursor to explicitly provided parameters,

positionally

cr_uid()

will automatically prepend the current cursor and user's id to explictly provided

parameters

cr_uid_ids()

will automatically prepend the current cursor, user's id and recordset's ids to explicitly

provided parameters

cr_uid_id()

will loop over the current recordset and call the method once for each record,

prepending the current cursor, user's id and record's id to explicitly provided parameters.

注意

如果采用这种修饰,新 API 上下文调用的方法返回值将永远返回一个列表

所有这些方法都有一个_context-后缀版本(例如 cr_uid_context()),它也通过关键字

传递当前上下文。

使用 v7()和 v8()的双重实现将被忽略,因为它们提供了自己的“桥接”

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

4. ODOO 视图 VIEW 开发手册

4.1 通用结构

视图对象定义了多个字段,除非特殊标注必填,不然它们是选填的。

name(必填)

一个视图描述,用于在一个视图列表中找出你所要的视图

model

在合适的场景指定视图所关联的模型,在 qweb 视图里就不是合适的场景

priority

客户端程序在获取视图的时候,一般是通过 id,也有可能是(model 模型, type 视图

类型)。

对于后者,我们将搜索出所有对应的(model, type)的视图,并取出 priority 值最

小的。这个视图便是这个(model, type)所对应的默认视图。

这个 priority 属性对于视图的继承先后顺序也生效。

arch

描述视图布局

groups_ids

一个 Many2many 的字段,指定某些可以访问这个视图的 group(群组)

inherit_id

当前视图所继承的视图,详细可以查看继承部分,默认是空的

mode

详细可以查看继承部分。

如果 inherit_id 为空,mode 只能是 primary;如果是继承来的视图,默认设置是

extension,但是也可以设置为 primary

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

application

网站功能 togglable 视图定义。默认情况下,视图总是被应用。

4.2 继承视图

4.2.1 获取视图

如果视图是有 model 和 type 所请求的,会获取到相对应(model, type)的,

mode=primary 的,priority 值最小的视图。

如果是由 id 发起的请求,如果 mode 不是 primary,那么会抓取它的 mode=primary 的

父类视图。

4.2.2 视图请求

请求会依照如下规范最终生成视图 arch:

如果视图有父视图,那么父视图将被完全解析,然后应用在当前视图;

如果视图没有父视图,那么它的 arch 是按这个视图所定义的去解析;。

如果当前视图有子视图且子视图的 mode=extension,那么它的继承规范应该是遵循深

度优先的原则(如果某个视图的子视图被解析,接下来这个子视图的子视图会先生效,然

后这个视图的其他子视图被解析)。

这个解析的最终结果将产生最终的 arch。

4.2.3 继承视图

继承视图由元素定位器组成,以获取父视图中的需要继承元素。子元素会被用于修改

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

继承元素。

有三种类型的元素定位器匹配目标元素:

expr: xpath 元素的 expr 属性,是一个 XPath 表达式,会找到当前 arch 的第一个满足

条件的节点;

一个带有 name 属性的字段元素,匹配第一个具有相同名称的字段。在匹配过程中,所

有其他属性都被忽略。

任何其他元素:匹配相同名称和相同属性(位置属性和版本属性不生效)的第一个元素。

继承视图可能有一个 position 位置属性,指定如何修改匹配的节点:

inside(默认):内容被附加到匹配的节点中

replace: 内容将替换匹配的节点。

after: 在匹配的节点之后,内容被添加到匹配节点的父节点中。

before: 在匹配的节点之前,内容被添加到匹配节点的父节点中。

attributes 继承的节点应该带有 attributes 属性:

如果这个 attribute 已经主体有值,会在匹配的节点上创建一个你写的属性名字的新

属性,并将属性元素的文本作为值创建;

如果没有值,则从匹配的节点中删除其名称后的属性。如果没有这样的属性,就会出

现错误。

视图是按一定顺序被解析安装的。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

4.3 列表视图

列表视图的根元素是<tree>。列表视图的根可以具有以下属性:

editable

默认情况下,选择 list 视图的行将打开相应的表单视图。

可编辑属性使列表视图本身可编辑。

可选值:top 和 bottom,使新记录分别出现在列表的顶部或底部。

form 视图里嵌套的 tree 视图也是从视图列表中派生出来的,大多数属性和按钮对这个

tree 视图也是起作用的。

如果这个视图是不可编辑的,有些属性可能会不起作用,没有意义。

default_order

重写视图的顺序,替换模型的默认顺序。该值是一个由逗号分隔的字段列表,由 desc

按顺序排序:

<tree default_order="sequence,name desc">

colors

自 9.0 版本以来已弃用:被一下装饰器取代:decoration-{$name}

fonts

自 9.0 版本以来已弃用:被一下装饰器取代:decoration-{$name}

decoration-{$name}

允许根据相应的记录的属性改变行文本的样式。

值是 python 表达式。

对于每个记录,表达式可以用记录的属性或者 context 进行评估,如果是满足条件,则将相

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

应的样式应用到行中。context 值是 uid(当前用户的 id 和 current_date(当前日期 yyyy-MM-dd 的

字符串)。

{$name}可以是 bf(font-weight: bold), it(font-style: italic),或者预先设置好的颜色样式

(danger,info,muted,primary,success 或者 warning).

create,edit,delete

允许在视图中禁用相应的操作,相应的属性默认设置为 false。

limit

页面的默认大小。它应该是一个正整数。

on_write

列表处于可编辑。列表模型中方法的名称。将在创建或编辑了该记录(在数据库中)之后,

使用当前记录 id 调用该方法。

该方法应该返回记录列表,用以加载或更新。

string

视图的可选可翻译标签。

列表视图的子元素:

button

在列表单元格中显示一个按钮。

icon

显示按钮的图标

string

如果没有 icon,是按钮的文本;如果有 icon,是 icon 的文本

type

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

按钮的 type,指示它如何影响 odoo:

object

在列表的 model 上调用一个 def 方法。按钮的 name 属性是方法名,该方法使用当前行的

记录 id 和 context 作为参数调用。

action

加载一个 ir.actions 的动作,按钮 name 是该动作的数据库 id,动作传递的 context 包含

active_model (当前列表视图的 model), active_id (当前行的记录 id), active_ids (当前列表中载入

的所有记录,可能只是与当前搜索匹配的数据库记录的子集)

name

详见 type

args

详见 type

attrs

基于记录值的动态属性。

根据 domain 里的条件,对当前记录进行判断,若满足条件,在当前行应用属性。

可能的属性是 invisible(隐藏按钮)和 readonly(禁用按钮但仍然显示它)

states

attrs 中 invisible 状态的一种简写模式:根据 state 字段进行判断,要求模型存在一个 state 字

段。如果当前记录的 state 不在列表中,那么记录被隐藏。

注意

使用在 attrs 里使用 states 字段可能会产生一些出乎意料的结果,因为 domain 结果

会和业务逻辑联系在一起

context

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

在执行按钮时的 context

confirm

在执行按钮的 Odoo 调用之前,要显示的确认消息(供用户确认)。

field

定义一个字段,这个字段会显示每一条记录里。

可以使用以下属性:

name

要在当前模型中显示的字段的名称。一个给定的名称只能在每个视图中使用一次。

string

字段的列标题(默认情况下,使用模型字段的字符串)

invisible

获取并存储字段,但不显示表中的列。针对一些界面上应该有的但不想展示的字段,

例:@ colors。

groups

列出应该能够看到该字段的组。

Widget

字段显示的形式。可能的列表视图值为:

progressbar

显示浮动字段作为进度条。

many2onebutton

如果字段有值,则用一个复选框替换 many2one 字段的值,如果没有,则使用×。

handle

对于序号的字段,不显示字段的值,而是显示一个拖放图标。

sum、avg

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

在字段的底部显示相应的聚合计算。聚合只在当前显示的记录上计算。

聚合操作必须匹配相应的字段的权限操作。

attrs

基于记录值的动态属性。只对当前字段产生影响。例如,在当前满足 domain条件的记录,

invisible 将隐藏字段,但在其他不满足条件的记录中,这个字段可见。

如果列表视图是 editable,那么表单视图中的任何字段属性都是有效的,并会在其

form 视图得到对应的值。

4.4 表单视图

表单视图用于显示来自单个记录的数据。它们的根元素是<form>。

它们由常规 HTML 语法和一些新增的语法结构组成。

4.4.1 结构组件

结构组件组成了 form 表单的可视化视图,这里逻辑功能很少。

它们通常用作表单视图中的元素或元素集合。

notebook

定义一个 notebook 页面标签。

notebook 通过页面的 page 元素定义不同的页面。页面可以具有以下属性:

string(必须的)

标签的标题。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

accesskey

HTML accesskey

attrs

基于记录值的标准动态属性。

group

用于定义表单中的列布局。默认情况下,组定义 2 列。每个最直接的子元素占用一个

列。field 类型的元素默认显示一个标签,而标签和字段本身都占一列。

可以使用 col 属性来定制组中的列数。

子元素是水平放置的。

组可以有一个 string 属性,它显示为组的标题。

newline

只在组元素内有用,将当前行提前结束,并立即切换到新的行(之前不填充任何剩余的

列)

separator

一条水平线,可以通过 string 属性来设置该区域的标题

sheet

可以用作 form 的子元素用来表示更加狭小的表单

header

与 sheet 一起使用,显示在 sheet 的上方的一个条,一般用于显示工作流和状态栏。

4.4.2 语法部分

语法与 Odoo 系统相关联并允许交互。

可用语义组件:

button

调用 Odoo 系统对应模型的方法,类似于列表视图按钮。此外,可以指定以下属性:

special

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

在对话框中打开表单视图:保存保存记录并关闭对话框,取消关闭对话框而不保存。

field

展示当前记录的某个字段,有以下属性:

name(必须)

呈现的字段的名称。

widget

每个字段根据其数据类型有一个默认的展示方式,widget 属性可指定用一个别的方式

来展示

options

用于指定 widget 字段配置的 json 对象

class

用于设置当前元素的 html class 属性:

oe_ inline:防止它自动将之后的字段换行

oe_left, oe_right:相当于 css 的 float 浮动

oe_read_only, oe_edit_only:只在相应的模式下展示该字段

oe_no_button:不为 many2one 字段显示导航按钮

oe_avatar:当该字段为图片时,将它展示为头像(90*90 的正方形)

groups

只将该字段展示给指定用户组

on_change

在字段值改变时调用对应方法,从 8.0 开始改用模型中的@api.onchange()

attrs

基于记录值的动态属性

domain

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

仅对关系字段,在显示现有记录时应用过滤器。

context

用于关联字段,获取可能的值时提供

readonly

该字段可在读和编辑模式下展示,但是永远是不能编辑的

required

当该值没有设置就保存时给出一个错误提示并阻止保存

nolabel

不显示字段的标签,只有在该字段是 group 子元素时有意义

placeholder

字段值为空时展示的提示

mode

对于 one2many 字段,用于展示其关联的记录的形式,有 tree, form, kanban, graph,

默认是 tree

help

当将鼠标放在字段或标签时显示的提示

filename

对于二进制的字段,关联该字段的文件名

password

该字段是一个密码,不明文展示

4.4.3 业务视图指南

业务视图针对的是普通用户,而不是高级用户。

例如: Opportunities, Products, Partners, Tasks, Project 等等模块。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

通常,业务视图由以下内容组成:

13. 顶部的状态栏(有技术或业务流程),

14. 中间的一张 sheet(表格本身),

15. 底部有历史和评论。

从技术上讲,新表单 xml 视图的结构如下:

<form>

<header> ... content of the status bar ... </header>

<sheet> ... content of the sheet ... </sheet>

<div class="oe_chatter"> ... content of the bottom part ... </div>

</form>

4.4.3.1 hearder 状态栏

状态栏的目的是显示当前记录的状态和可操作按钮。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

4.4.3.1 button 按钮

按钮的顺序遵循业务流程。例如,在销售订单中,逻辑步骤如下:

1. 发送报价(Send the quotation)

2. 确认报价(Confirm the quotation)

3. 创建最终的发票(Create the final invoice)

4. 发送货物(Send the goods)

高亮显示的按钮(默认为红色)帮助用户辨别下一步的逻辑按钮,它通常是第一个活动

按钮。

另一方面,取消按钮必须保持灰色。例如,在发票中,按钮退款不能是红色的。

技术上,通过添加类“oe_highlight”来突出显示按钮:

<button class="oe_highlight" name="..." type="..." states="..."/>

4.4.3.2 STATE 状态

使用 widget=”statusbar”的部件,会以红色显示当前状态。所有流程的通用状态(例如,

销售订单以报价开始,然后我们发送它,然后它成为一个完整的销售订单,最后它完成了)

应该在任何时候都可以看到,但是根据特定子流的异常或状态只应该在当前的情况下可见。

<field name="state" widget="statusbar"

statusbar_visible="draft,sent,progress,invoiced,done" />

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

state 中的排列顺序是参考字段定义的顺序(Selection 字段中的列表)。

状态是否可见是由 statusbar_visible 属性指定的。

<field name="state" widget="statusbar"

statusbar_visible="draft,sent,progress,invoiced,done" />

4.4.3.2 sheet

所有的业务视图应该看起来像一个印刷的表格:

页面元素按照正常的 HTML 规则在一个<form>或者<page>中列出,可以使用<div>或者

<group>对它们进行显式分组。

默认情况下,<group>定义了内部的两列,除非使用了属性 col="n"。这些列具有相同

的宽度(组宽度的 1/n)。使用<group>把字段列包在其中。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

1. 定义一个<group>元素的时候,增加一个 string 属性作为这部分的标题。

<group string="Time-sensitive operations">

2. <field>元素不会产生 label,除非它是<group>的子元素。

可以使用<label for="field_name>生成 field 的 label。

4.4.3.2.1 表单状态栏

有些表单有一个或多个字段的标题,这些字段的标签只显示在编辑模式中。

使用 HTML 文本,<div>,<h1>、<h2>…产生漂亮的标题

class=”oe_edit_only”的<div>去定义字段的 label,这样在编辑模式下才会显示字段的

标签。

oe_inline 的样式:使字段处于同一行。

可以参考下下面的 xml:

<label for="name" class="oe_edit_only"/>

<h1><field name="name"/></h1>

<label for="planned_revenue" class="oe_edit_only"/>

<h2>

<field name="planned_revenue" class="oe_inline"/>

<field name="company_currency" class="oe_inline

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

oe_edit_only"/> at

<field name="probability" class="oe_inline"/> % success rate

</h2>

4.4.3.2.2 按钮箱

许多相关的操作或链接可以显示在表单中。例如,在机会形式中,安排一个电话

(Schedule a Call)和安排会议(Schedule a Meeting)这两个动作在 CRM 的使用中占有重要

地位,不要把它们放在“更多”菜单中,而是直接把它们放在页面上,作为按钮(在顶部),

让它们更显眼,更容易访问。

从技术上讲,这些按钮被放置在一个<div>中,将它们作为一个块放在页面的顶部。

<div class="oe_button_box" name="button_box">

<button string="Schedule/Log Call" name="..." type="action"/>

<button string="Schedule Meeting" name="action_makeMeeting"

type="object"/>

</div>

4.4.3.2.3 权限组

字段一般放在<group>标签元素里,可选择的带上 group 的标题

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<group string="Payment Options">

<field name="writeoff_amount"/>

<field name="payment_option"/>

</group>

为了使视图的继承变得更简单,建议在<group>上设置 name 属性,以便在正确的位置

添加新字段。

特殊情况:小计

一些样式被定义为小计汇总,例如发票的 form:

<group class="oe_subtotal_footer">

<field name="amount_untaxed"/>

<field name="amount_tax"/>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<field name="amount_total" class="oe_subtotal_footer_separator"/>

<field name="residual" style="margin-top: 10px"/>

</group>

4.4.3.2.4 占位符与同行字段

有时,字段标签会使表单过于复杂。

一种做法是忽略字段标签,而是在字段中放置一个占位符。当字段为空时,占位符文

本才可见。占位符应该告诉在字段中放置什么,它不能是示例数据,因为它们常常与真实

的数据混淆,只能是一个提示文本。

还有一种做法,可以通过在用作显示的元素块(比如<div>)中 inline 的方式将字段组合

在一起,这会将相关的字段按照实际业务需求分组,就像它们是单个(复合)字段一样。

下面的示例摘自 lead 的 form,注意看字段(zip 和 city)。

<group>

<label for="street" string="Address"/>

<div>

<field name="street" placeholder="Street..."/>

<field name="street2"/>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<div>

<field name="zip" class="oe_inline" placeholder="ZIP"/>

<field name="city" class="oe_inline" placeholder="City"/>

</div>

<field name="state_id" placeholder="State"/>

<field name="country_id" placeholder="Country"/>

</div>

</group>

4.4.3.2.5 图像

图像,比如头像,应该显示在 form 的右边。例如产品的 form 视图:

上面的表单包含一个在一个<sheet>元素,结构如下:

<field name="product_image" widget="image" class="oe_avatar oe_right"/>

4.4.3.2.6 标签

大多数的 many2many 字段,比如 categories,使用标签的列表来呈现效果会更好。可

以使用这个 many2many_tags 小部件,例如:

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<field name="category_id" widget="many2many_tags"/>

4.4.4 系统配置

很多系统配置(比如状态、类型等等)的菜单,应该放在对应模块的配置菜单下,比如

Sales/Configuration,一般没有<sheet>和<header>

1. 不需要状态栏(因为不存在 state 字段,工作流或者按钮)

2. 不需要表单

4.4.5 对话框须知

例子:opportunity 的 Schedule a Call

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

1. 避免 separators 分隔符(标题已经在标题栏中,所以不需要分隔符);

2. 避免 cancel 按钮(用户可能会点击这个按钮来关闭弹框)

3. 按钮需要高亮;

4. Text 字段需要设置 placeholder

5. 把按钮放在状态栏中

4.4.6 wizard 向导须知

例子:Settings 的 Configuration 的 Sales

wizard 的 form 不能再有 wizard

不用<sheet>

必须有 cancel 按钮

应用按钮必须设为红色

4.5 透视图表 GRAPH

图形视图用于在多个记录或记录组中可视化聚合。它的根元素是<graph>,它可以使

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

用以下属性:

type:bar(默认),pie 和 line,使用的图形类型。

stacked:type=bar 时块的叠加

graph 视图中唯一允许的元素是 field,可以具有以下属性:

name(必须): 在 graph 视图中使用的字段的名称,更多的用于分组,而不是聚合

type:指示字段是否应作为分组标准使用,或作为组内的聚合值。可能的值是:

row(默认):按指定字段分组。所有图形类型支持至少一个级别的分组,有些可能支持

更多。

对于 pivot 视图,每个组都有自己的行。

col:仅由 pivot 使用,创建列。

measure: 字段聚合。

interval: 在 date 和 datetime 字段中,按指定的时间间隔(day, week, month, quarter

或者 year)进行分组

提示

执行 graph 视图聚合,不能使用数据库不存在的字段。

4.5.1 Pivot

pivot 视图用于将数据聚合可视化为一个透视表。

它的根元素是<pivot>,它可以使用以下属性:

disable_linking: 设置为 True,删除到列表视图的关联。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

display_quantity:设置为 True,默认显示 Quantity 列。

允许 pivot 和 graph 的视图元素完全一样

4.6 看板视图 KANBAN

将记录显示为卡片,介于列表视图和非可编辑表单视图之间。

可以将记录分组在列中,以便用于工作流可视化或操作.

看板视图的根元素是<Kanban>,它可以使用以下属性:

default_group_by:如果看板的搜索视图和动作都没有指定分组,会根据这个属性的

值(一个字段名)去分组

default_order:如果没有定义 order,则使用 default_order 去排序

class:应用 class 样式

group_create:是否可以增加新列,默认 True

group_delete:是否可以通过上下文菜单删除组,默认 True

group_edit:分组是否可以通过上下文菜单进行编辑,默认 True

quick_create:是否可以在看板视图快速创建记录,默认情况下,当看板视图设置了

group 时启用。可能用到的子元素:

field

声明在看板逻辑中使用的字段。如果字段仅仅显示在看板视图中,则不需要预先声明。

可能的属性是:

name(必填)

要抓取的字段的名称。

progressbar

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

声明一个 progressbar 元素放在看板列上。可能的属性是:

field(必需)

用于在 progressbar 中对列的记录进行分组的字段的名称。

color(必需)

JSON 将上述字段值映射为“danger 危险”、“warning 警告”或“success 成功”相

对应的样式颜色。

sum_field(可选)

字段的名称,其列的记录值将被求和并显示在 progressbar 旁边(如果省略了,则显

示记录的总数)

templates

定义一个 QWeb 模板列表。可以将一个模板定义拆分为多个模板,以使其更加清晰,

但是看板视图必须定义至少一个根模板,它将为每个记录呈现一次。

看板视图使用大多数标准的 javascript qweb 并提供一下部件:

Widget

当前的 KanbanRecord()可以用来获取一些元信息。这些方法也可以直接在模板上下

文中使用,不需要通过小部件访问。

Record

一个具有所有请求字段作为属性的对象。每个字段有两个属性值和 raw_value,前者

根据当前用户参数进行格式化,后者是来自 read()的直接值(除了日期和 datetime 字段,

这些字段根据用户的语言环境进行格式化)

read_only_mode

只读

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

4.6.1 按钮和字段

虽然大多数看板模板都是标准的 QWeb,但看板视图会特殊地去处理字段、按钮和元

素:

默认情况下,字段被格式化值替换,除非它们匹配特定的看板视图小部件。

带有 type 属性的 button 和 link 将执行与 odoo 相关的操作,而不是标准的 HTML 函

数。可能的类型是:

action, object

Odoo 按钮的标准行为,大多数与标准 Odoo 按钮相关的属性都可以使用。

Open

以只读模式打开表单视图中的卡片记录。

edit

在可编辑模式下打开表单视图中的卡片记录。

delete

删除卡片的记录并删除卡片。

如果您需要扩展看板视图,请参见:js:class::the JS API <KanbanRecord>

4.7 日历视图 CALENDAR

日历视图将记录显示为每日、每周或每月日历中的记录。它们的根元素是<calendar>。

日历视图中可用的属性如下:

date_start(必需):

记录该事件的开始日期的记录字段的名称。

date_stop:

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

记录的字段名称为事件的结束日期,如果 date_stop 被提供,则记录可以直接在日历

中通过拖放移动。

date_delay:

替代 date_stop,提供事件的持续时间,从而计算出事件的结束日期(单位:日)

color:

提供需要用于颜色区分的字段。该字段值相同的记录在日历中会被分配相同的高亮颜

色,颜色是半随机分配的。在侧栏中显示字段值和对应的颜色。

readonly_form_view_id:

视图以只读模式打开。

form_view_id:

视图在用户创建或编辑事件时打开。

event_open_popup:

如果“event_open_popup”被设置为 true,那么日历视图将在 FormViewDialog 中打

开事件(或记录)。否则,它将在一个新的表单视图中打开事件(使用 do_action)

quick_add:

在单击时启用快速事件创建:只向用户询问一个名称,并尝试用该名称和单击事件时间

创建一个新事件。如果快速创建失败,则返回到完整的表单对话框。

all_day:

在记录上的布尔字段的名称,指示相应的事件是否被标记为一天(与持续时间无关)

mode:

载入日历时默认显示模式。可能的属性是: day, week, month

field:

将字段做聚合或在看板逻辑中使用。字段会显示在日历卡片中。

字段可以有以下属性:

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

invisible:

为 True 隐藏

avatar_field:

对 于 one2many 和 many2many 字 段 , 显 示 具 体 的 值 , 而 不 是 在 卡 片 中 显 示

display_name。

write_model 和 write_field:

可以添加一个过滤器,并将结果保存在已定义的模型中,在侧栏中添加过滤器。

templates:

定义 QWeb 模板日历框。

卡片定义可能被分割成多个模板,以便为每个记录呈现一次。

看板视图使用大多数标准的 javascript qweb 并提供以下上下文变量:

widget:

当前的 KanbanRecord()可以用来获取一些元信息。这些方法也可以直接在模板上下文

中使用,不需要通过小部件 getColor 访问,以转换为一个颜色整数 getAvatars,以转换为

化身图像 displayFields 列表,而不是看不见的字段。

record:

一个具有所有请求字段作为属性的对象。每个字段有两个属性值和 raw_value

event:

日历事件对象

format:

将值转换为具有用户参数的可读字符串的格式方法。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

fields:

所有模型字段的定义。

user_context:

自定义上下文

read_only_mode:

只读模式。

4.8 甘特视图 GANTT

Gantt 视图提供甘特图(用于调度)。

gantt 视图的根元素是<gantt/>,它没有子元素,但是可以使用以下属性:

date_start(必需):

为每个记录提供事件的开始日期时间的字段的名称。

date_stop:

为每个记录提供事件结束持续时间的字段的名称。可以被 date_delay 替换。

date_start 集和 date_start 必须有且仅有一个。。如果字段为记录错误,则假定为

“point 事件”,结束日期将设置为开始日期。

date_delay:

提供事件持续时间的字段名。

duration_unit:

一分钟,一小时(默认),天,星期,月,年。

default_group_by:

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

默认分组

type:

gantt:

经典甘特图(默认)

consolidate:

第一个 child 的值被合并甘特图任务中

planning:

children 会自动显示到甘特图任务中

consolidation:在记录单元格中用于显示合并值的字段名

consolidation_max:数据字典,表示超过一定的值会标红显示 ,如:{"user_id": 100}

提示

字典内必须使用双引号,{'user_id': 100} 就不是一个有效值

string:

展示在合并值旁边的字符,如果没设置会自动取对应字段的 label

round_dnd_dates:

开始和结束时间取整

drag_resize:

任务调整,默认 True

4.9 图形视图 DIAGRAM

diagram 视图可用于展示记录的 directed edge of graph,根元素是<diagram>。

可用的自元素有:

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

node (必填):

定义了图像的节点,它的属性有:

object:

该节点对应的 odoo 模型

shape:

节点形状,类似于列表视图中的 colors 和 fonts。唯一有效的形状是 rectangle (默

认形状是一个省略号)

bgcolor:

节点背景颜色,与 shape 类似。默认值是白色,另一个有效值是 grey。

arrow(必填):

定义了 directed edges of the graph,它的属性有:

object(必填):

该节点对应的 odoo 模型

source(必填):

一个 edge 模型的 Many2one 的数据源字段

destination(必填):

一个 edge 模型的 Many2one 的数据目的字段

label:

一个 python 的字符串列表,对应的参数值将在标签中展

label:

diagram 的备注,string 参数定义了该备注的内容。每个 label 将作为 diagram 状态栏的一

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

部分展示,容易看到,但无法强调内容。

4.10 搜索视图 SEARCH

与以前的视图类型相比,搜索视图并不显示内容。虽然它们同样应用于特定的模型,

但它们被用来过滤其他视图的内容(聚合作用的视图,例如列表或图形)。除了使用场景的

不同之外,它们的定义是相同的。

根元素是<search>,不接受任何属性。

可用的子元素有:

field:

搜索条件的基本组成部分。在生成搜索域时,字段域使用 AND 连接若干个 field。

name:

需要过滤的字段名称。

string:

该搜索条件的标签。

operator:

在默认情况下,字段生成表单的域[(name、operator、provided_value)],其中 name 是

字段的名称,provided_value 是用户提供的值,可能是经过筛选或转换的(例如,用户可能

会输入 Selection 字段的标签,而不是值)。

operator 参数允许依据字段的类型重写默认的匹配逻辑(例如 float 型字段的=匹配与

char 型字段的 ilike 匹配)

filter_domain:

用于提供完整的域作为字段的搜索域,可以使用 self作为 provided_value。可以用来

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

生成比 operator 更灵活的域(例如,同时在多个字段上进行搜索)

如果提供了 operator 和 filter_domain,则 filter_domain 优先。

context

允许添加上下文键,也可以添加用于筛选的值(作为 self 的一个搜索条件)。默认为

空。

domain 条件和 context 并不冲突。要想仅根据上下文进行筛选,请将 filter_domain 设

置为空列表:filter_domain="[]"

groups:

是字段仅对指定用户可见。

widget:

对该字段使用一个特定的搜索向导(唯一的使用场景是 odoo8.0 的 Many2one 字段的

Selection 向导)

domain:

如果字段的备选值由 odoo 实现(例如 Many2one 字段),对这些备选值设置搜索域。

filter:

过滤器是在搜索视图中预定义的一个切换开关,它只能被启用或禁用。它的主要目的

是将数据添加到搜索上下文(用于搜索/过滤的数据视图的上下文),或者将新的条件添加到

搜索过滤器中。

过滤器的参数有:

string(必填):

过滤器的标签。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

domain:

搜索域,启用时将被一并并入搜索条件中。

context:

上下文,启用时将被一并并入上下文中。

name:

过滤器的逻辑名称,可以使用该名称默认开启,也可以被继承重写。

help:

筛选器的解释说明,可以用于展示。

groups:

使筛选器只对指定用户可用。

7.0 的新特性

筛选器条件如果相互之间不存在非其他标签,他们将执行‘或逻辑’。例如:

<filter domain="[('state', '=', 'draft')]"/>

<filter domain="[('state', '=', 'done')]"/>

如果筛选器都被启用,那么查找 state 是 draft 或者 done 的记录,但是

<filter domain="[('state', '=', 'draft')]"/>

<separator/>

<filter domain="[('delay', '<', 15)]"/>

如果筛选器都被启用,那么查找 state 是 draft 且 delay 小于 15 的记录

separate

用于在简单地视图里间隔筛选器。

group

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

用于间隔不同组别的筛选器,在复杂视图中比 separator 更具可读性。

4.10.1 Search 默认条件

搜索视图中的<field>和<filter>可以通过动作中的上下文中‘search_default_name’

的键值对调用。对于 field 来说,值应该是一个该字段的有效值;对于 filter 来说,应该

是一个布尔值。比如,假设 foo 是一个 field,bar 是一个 filter:

{

'search_default_foo': 'acro',

'search_default_bar': 1

}

将自动启用 bar 筛选器,并执行 foo 字段的筛选。

4.11 打印视图 QWEB

QWeb 视图是视图的 arch 内的标准 QWeb 模板。它们没有特定的根元素。

QWeb view 只能包含一个 template,模板的名称必须匹配视图完整的(包括模块名)外部 id。

模板应该被用作定义 QWeb view 的快捷方式。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

5.THEME TUTORIAL 主题教程

Odoo 支持用户自定义模块。用户可以根据自己的需求定制所有的东西。

首先,开始创建自定义的主题。在开始之前,请你了解:本教程是创建一个 Odoo 主

题的指南。

5.1WEB 设计工具介绍

如果你是第一次使用 odoo 的网页设计工具,那你找对了。这个教程将概述 odoo 主题

的创建基础。

因为 Odoo 的团队创建了一个强大且易于使用的框架。不过我们没有必要去了解使用

这组工具的特殊语法。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

5.1.1 从普通的内容管理系统(CMS)到 odoo

如果你一直用同样的方式思考和工作,你只能得到同样的结果。如果你想要一些全新

的东西,那么你就得尝试不同的方式。

“我的 header.php 文件在哪里?”

习惯使用 Wordpress 或 Joomla 的网页设计师,在第一次使用 Odoo 时通常会发出上

面的疑问。

实际上,在使用通用的CMS系统时,必须通过编写

几个文件(比如 header.php,page.php,post.php 等等)为

你的网站创建一个基本结构。使用这些系统时,这个基

础结构就成为了一个您必须随时更新,以确保CMS内兼

容性的设计基础。所以,即使你花了几个小时编写文件,可能还没有开始设计。

所以这并不适用于创建 Odoo 主题。

我们认为主题设计应该是简单的和功能性强的。当我们创建浏览器网站时,我们决定

不依赖既有的,而是从新创建。这种方法让我们可以自由地关注那些对浏览器来说非常重

要的元素,比如:样式、内容和逻辑。并且不再纠结于其他技术问题。

5.1.2 odoo 默认的主题结构

Odoo 有一个默认的基础“主题”结构。它提供了最基础的结构和布局。当创建一个

新主题时,本质上是在扩展这个主题。实际上,它总是在设置中启用并且这个主题与上面

提到的 CMS 的基本结构完全一样,只是用户不必创建或维护它。因为它包含在网站管理员

模块下,所以主题结构在默认情况下将会在 Odoo 模块安装中自动升级。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

因此,虽然这种结构可以提供集成功能,但是设计者完全可以有更多时间专

注于设计。

主要特征:

• 页面、博客和电子商务的基

本布局

• 网页浏览器集成

• 基本的片段

• 自动/Sass 编译

• 自动 JS 和 CSS 的简化和组

合。

主要技术:

• Twitter Bootstrap

• jQuery

• jQuery UI

• underscore.js

5.2 思考 "模块化"

Odoo 主题不是由一个包含 HTML 或 PHP 文件的文件夹构成,而是用 XML 编写的模

块化框架。“之前没使用过 xml 文件?”不用担心,在学习了教程之后,只需要基本的

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

HTML 知识,就能够创建您的第一个主题。

通常会使用经典的 web 设计流程去构造整个页面的布局,最后得到了一个“静态”

web 页面。当然,现在你可以更新内容,但你的客户会要求你做一些基础的改动。

创建 Odoo 主题则是一个完全不同的视角。不需要为页面定义完整的布局,您可以创

建块(片段),让用户选择在哪里“拖放”它们,创建自己的页面布局。我们称之为模块化

设计。

可以把 Odoo 主题看成必须创建的风格或元素组成的“列表”。作为一个设计器,您

的目标是渲染这些网页元素,无论最终用户选择将它们放在哪里,都能达到一个完美的结

果。

让我们来参观一下我们的“列表”元素:

片段

片段是一段 HTML 代码。用户将使用

我们内置的网站管理员模块中的界面来拖

拽、修改和组合它们。您可以为每个代码

片段定义一组选项和样式。用户将根据需

要选择他们。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

页面

这些 web 页面除了它们将由最终用户

编辑,其他都是一般的 web 页面。并且您

可以定义一个空白区域,用户可以通过将

代码片段拖动到其中来“填充”。

样式

样式是使用标准的 CSS 文件或 Sass 来

定义的。可以将样式定义为默认样式或可

选样式。默认样式在您的主题中始终是显

示的,可选择的样式可以由用户启用或禁

用。

功能

由于 Odoo 的模块化,所有的东西都

可以更加个性化。这意味着你可以创造你

想要的功能。并且添加功能很简单,比如

为最终用户定制个性化的选项功能也很容

易。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

5.2.1 Odoo 的 xml 文件和概述

任何 Odoo 的 XML 文件都是从编码规范开始的。在<odoo>标签之后,必须在标记中编

写代码。

[XML]

<?xml version="1.0" encoding="utf-8" ?>

<odoo>

## YOUR CODE HERE

</odoo>

您创建的几乎所有元素和选项都必须放置<template>标签中,如下。

[XML]

<template id="my_title" name="My title">

<h1>This is an HTML block</h1>

<h2 class="lead">And this is a subtitle</h2>

</template>

注意

不要误解模板的意思,模板标签只定义了一段 html 代码和选项,但他并不一定与元素

的网页排列相一致。

前面的代码定义了一个标题,但它不会在页面任何地方显示,因为该模板与 Odoo 默认

结构的任何部分没有关联。为了做验证这一点,您可以使用 xpath、qWeb 或两者的组合。

继续阅读教程了解如何正确地使用您自己的代码去扩展它。

5.2.2 更改主题

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

由于 XML 文件只在安装主题时加载,所以每次在 XML 文件上进行更改时,都必须重

新升级加载。为了升级模块,请单击模块页面中的 Upgrade 按钮。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

5.3 创建一个主题模块

Odoo 的主题被打包成模块。即使你为你的公司或客户设计一个非常简单的网站,你也需要像 odoo 模

块一样地包装主题。

主文件夹

创建一个文件夹并将其命名为: theme_ 后跟你的主题名称。

__manifest__.py

创建一个空的文件并且保存命名为 __manifest__.py

这个文件中将包含主题的配置信息。

__init__.py

创建另一个空的文件并命名为__init__.py,它是一个强制性的系统文件,创建并保持空白。

views and static folders 视图和静态文件夹

在主文件夹中创建它们。在视图文件夹中,您将放置您定义的代码段、页面和其他文件的 xml 文件。

static 文件夹是包含您的样式、图片和 js 代码的地方。

注意

在开始时使用两条下划线字符,在 odoo 和 init 文件的标题末尾使用两个下划线。

最后的结果如下图所示:

5.3.1 编辑 __manifest__.py

打开__manifest__.py 如下所示复制粘贴代码创建模型。

{ 'name':'Tutorial theme', 'description': 'A description for your theme.', 'version':'1.0', 'author':'Your name', 'data': [ ], 'category': 'Theme/Creative', 'depends': ['website'],

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

}

前四个字段属性的值可以被随意替换。这些值将用于在 Odoo 识别模块新主题。

data 属性将包含 xml 文件列表。现在它是空的,但是我们将添加任何创建的 xml 文件。

category 定义了您的模块所属类别(“主题”),并在一个斜杠之后定义了子类别。您可以从 Odoo 应用

程序类别列表中使用一个子类。(https://www.odoo.com/apps/themes)

depends 后面跟依赖的指定模块,这样我们的主题才能正常工作。对于我们的主题教程,我们只需要

website 模块。如果你需要博客或商城功能,你也需要添加这些相应模块。

... 'depends': ['website', 'website_blog', 'sale'], ...

5.3.2 安装你的主题

要安装您的主题,您只需将您的 theme 文件夹放置在 Odoo 安装的插件中。然后,转换到 Settings 页面,

查找主题并单击 install 按钮。

5.4 一个 ODOO 页面的结构

一个 odoo 页面是由两种元素组合的独特效果。默认情况下,odoo 为用户提供了一个页眉<header>,一

个页脚<footer>(可跨页面)和一个主元素标签<main>,使得页面的效果唯一。

跨页面元素在每个页面上都有效果。唯一的元素只在特定的页面有效。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

要检查默认布局,只需使用网站构建器创建一个新页面。点击内容‣新的页面并添加一页名。再使用浏

览器检查页面。

<div id=“wrapwrap”> <header /> <main /> <footer /> </div>

5.4.1 扩展默认 Header

默认情况下,Odooo 头部包含一个响应式的导航菜单和公司 logo。您可以轻松地在上面增加新元素,

或者修改现有元素。

为此,创建一个布局。在视图文件夹中添加 layout.xml 文件并添加默认的<Odoo> 标记。

<?xml version="1.0" encoding="utf-8" ?> <odoo> </odoo>

在<odoo>标记中创建一个新模板,并复制下列的代码。

<!-- Customize header --> <template id="custom_header" inherit_id="website.layout" name="Custom Header"> <!-- Assign an id --> <xpath expr="//div[@id='wrapwrap']/header" position="attributes"> <attribute name="id">my_header</attribute> </xpath>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<!-- Add an element after the top menu --> <xpath expr="//div[@id='wrapwrap']/header/div" position="after"> <div class="container"> <div class="alert alert-info mt16" role="alert"> <strong>Welcome</strong> in our website! </div> </div> </xpath> </template>

第一个 xpath 是向标题添加 id 等于 my_header。如果您希望将 css 规则定位到该元素,并避免这些影

响到页面上的其他内容,那么这是最好的选择。

警告

小心替换默认元素属性,当主题将扩展默认值时,您的更改将在未来优先考虑。

第二个 xpath 将在菜单后添加一条 welcome 信息,最后一步是添加页面布局,将 xml 文件添加到

_manifest.py 文件中的 data,如下所示。

'data': [ 'views/layout.xml' ],

更改主题

我们成功地在导航菜单后给 header 添加 id 和元素,这个改变将并用于每一个网页。

5.5 创建一个特定的页面布局

假如我们想为页面创建一个特定的布局。对于这个页面,我们需要向顶部添加一个列表并且通过给客

户端使用代码片段设置页面。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

在您的视图文件夹中,创建一个 page.xml 文件并添加默认的 Odoo 标记。在<odoo>中创建一个

<template>标签,将页面属性设置为 true 并将代码复制其中。

<?xml version="1.0" encoding="utf-8" ?> <odoo> <!-- === Services Page === --> <template name="Services page" id="website.services" page="True"> <h1>Our Services</h1> <ul class="services"> <li>Cloud Hosting</li> <li>Support</li> <li>Unlimited space</li> </ul> </template> </odoo>

页面的标题将成为 template 的 ID。在我们 from website.services 案例服务中

我们成功地创建一个新的页面布局,但是系统不知道怎么使用它。为了让系统识别,我们使用 QWeb。

如下图所示,将 html 代码封装到标签中。

<!-- === Services Page === --> <template name="Services page" id="website.services" page="True"> <t t-call="website.layout"> <div id="wrap"> <div class="container"> <h1>Our Services</h1> <ul class="services"> <li>Cloud Hosting</li> <li>Support</li> <li>Unlimited space</li> </ul> </div> </div> </t> </template>

使用<t t-call="website.layout">我们将用这个代码扩展 Odoo 的默认页面布局。

如下所示,我们将代码放在两个<div>标签里面,其中一个 id 为 wrap ,另一个的 class 为 container。

这就是最基本的一个布局。

下一步是添加一个空白区域,用户可以在其中填充代码片段。要实现这一点,只需在<div id=”wrap”>

元素的闭区间之前使用 oe_structure 类创建一个<div>。

<?xml version="1.0" encoding="utf-8" ?> <odoo> <!-- === Services Page === --> <template name="Services page" id="website.services" page="True"> <t t-call="website.layout">

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

<div id="wrap"> <div class="container"> <h1>Our Services</h1> <ul class="services"> <li>Cloud Hosting</li> <li>Support</li> <li>Unlimited space</li> </ul> <!-- === Snippets' area === --> <div class="oe_structure" /> </div> </div> </t> </template> </odoo>

你可以创建尽可能多的代码片段,并把它们放在页面的任何位置。

我们的页面差不多准备好了。现在我们要做的就是把页面文件 pages.xml 添加在__manifest__ .py 文件

中。

'data': [ 'views/layout.xml', 'views/pages.xml' ],

更新你的主题

Our services 页面已经准备好了,你就可以通过导航到 /yourwebsite/page/services

访问它。您将注意到,可以在我们的服务列表下面拖放一些代码片段。

现在让我们回到我们的 page.xml,并且在我们的页面模板之后,复制/粘贴以下代码。

<record id="services_page_link" model="website.menu"> <field name="name">Services</field> <field name="url">/page/services</field> <field name="parent_id" ref="website.main_menu" /> <field name="sequence" type="int">99</field> </record>

这个代码将与主菜单联系在一起。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

sequence 序列属性定义了链接在顶部菜单中的位置。在上图的示例中,我们将值设置为 99,以便将

序列放置到最后。如果想把它放在一个特定的位置,你必须根据你的需要来替换它的序列值。

如图所示。在网站模块中的 data.xml 文件中检查数据,将 Home 链接设置为 10,默认设置为 60。

例如,如果您想将链接放在中间,您可以将链接的序列值设置为 40。

5.6 添加样式

在默认情况下,Odoo 自带 Bootstrap。这意味着您可以使用 Bootstrap 所有的样式和布局功能。

当然,如果您想完成一个独特的设计,Bootstrap 是不够的。下面的步骤将指导您如何向主题添加自

定义样式。虽然最终的结果不会很完美,但您学到足够的知识去构建自己的设计。

让我们首先创建一个名为 style.less 的空文件。将文件放入 static 文件夹里的 less 文件夹。下面的代码

将渲染我们的 Service 页面。复制并粘贴它,然后保存文件。

.services { background: #EAEAEA; padding: 1em; margin: 2em 0 3em; li { display: block; position: relative; background-color: #16a085; color: #FFF; padding: 2em; text-align: center; margin-bottom: 1em; font-size: 1.5em; } }

虽然我们的文件已经准备好了,但是还没有包含在我们的主题中。

所以让我们转向视图文件夹并创建一个名为 assets.xml 的 XML 文件。添加默认的 Odoo xml 标记并复

制/粘贴以下代码。记住要用主文件夹名替换代码中的 theme folder。

<template id="mystyle" name="My style" inherit_id="website.assets_frontend"> <xpath expr="link[last()]" position="after"> <link href="/theme folder/static/less/style.less" rel="stylesheet" type="text/les

s"/> </xpath> </template>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

我们刚刚创建了一个模板,用来指定 style.less 文件。如您所见,我们的模板有一个特殊的属性,称为

inherit_id。这个属性告诉 Odoo,我们的模板引用另一个模板来实现效果。

在本例中,我们继承了 website 模块的 assets_frontend 模板。这个模板以列表形式决定了网站构造

器加载的素材。我们的目标是将 style.less 添加到这个列表中。

可以使用 xpath 中的属性 attributes expr="link[last()]"和 position="after"来达到这个目的。这就

是在告诉 Odoo“获取我的样式文件,并将其放在素材列表的最后一个引用之后”。

把它放在最后一个引用之后,确保我们的文件将在最后被加载并优先加载。

最后添加 assets.xml 在你的 __manifest__.py 文件。

更新主题

我们的 style.less 文件现包含在主题中,它将被自动编译并与所有的 odoo 元素相结合。

5.7 创建代码片段

由于代码片段是用户设计和布局页面的方式,所以它们是设计中最重要的元素。让我们为服务页面创

建一个代码片段。该代码片段将显示三条评价,最终用户将使用 Website Builder UI 进行编辑。现在转向视

图文件夹并创建一个名为 snippets.xml 的 XML 文件。添加 Odoo 中默认的 xml 标记并复制/粘贴以下代码。

模板包含由代码片段写入的 HTML 标记。

<template id="snippet_testimonial" name="Testimonial snippet"> <section class="snippet_testimonial"> <div class="container text-center"> <div class="row"> <div class="col-md-4"> <img alt="client" class="img-circle" src="/theme_tutorial/static/src/img/client

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

_1.jpg"/> <h3>Client Name</h3> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> </div> <div class="col-md-4"> <img alt="client" class="img-circle" src="/theme_tutorial/static/src/img/client

_2.jpg"/> <h3>Client Name</h3> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> </div> <div class="col-md-4"> <img alt="client" class="img-circle" src="/theme_tutorial/static/src/img/client

_3.jpg"/> <h3>Client Name</h3> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> </div> </div> </div> </section> </template>

如上图所示,页面的以上三列<div>使用了 Bootstrap 默认的样式。它们不仅仅与页面布局有关,这些

样式将由网页构造器运行,并且它们可以进行手动调整。

上图的代码将创建代码片段的内容,但是我们仍然需要将其放置到编辑器栏中,这样用户就可以将它

拖放到页面中。在您的代码片段中复制/粘贴这个模板到 snippets.xml 文件。

<template id="place_into_bar" inherit_id="website.snippets" name="Place into bar"> <xpath expr="//div[@id='snippet_content']/div[@class='o_panel_body']" position="inside

"> <t t-snippet="theme_tutorial.snippet_testimonial" t-thumbnail="/theme_tutorial/static/src/img/ui/snippet_thumb.jpg"/> </xpath> </template>

使用 xpath,我们的目标是继承使用 id 等于 snippet_structure 的特定元素。这意味着代码片段将出

现在 Structure 选项卡中。如果您想要更改目的选项卡,您只需替换 xpath 表达式中的 id 值。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

选项卡名称 Xpath 表达式

Structure //div[@id='snippet_structure']

Content //div[@id='snippet_content']

Feature //div[@id='snippet_feature']

Effect //div[@id='snippet_effect']

标记<t>将调用我们的代码片段的模板,并将在 img 文件夹中分配一个小图片的位置。现在可以从片

段栏中拖动代码片段,将其放到页面中并查看结果。

5.8 代码片段的选项

选项功能允许浏览器使用网站构造器的 UI 编辑代码片段的外观。使用网站构造器功能,您可以轻松创

建片段选项并且自动将他们添加到 UI。

5.8.1 选项组属性

options 参数以列表形式出现。可以通过定义 option 属性来来控制客户端页面的一些表现。

选项用组包装。组可以拥有定义选项如何与用户界面交互的属性。

data-selector="[css selector(s)]"

将组中的所有选项绑定到一个特定的元素

data-js=" custom method name "

用于绑定定制的 javascript 方法

data-drop-in="[css selector(s)]"

定义可以将代码片段放入其中的元素列表

data-drop-near="[css selector(s)]"

定义代码段可以放在旁边的元素列表

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

5.8.2 默认的选项方法

options 属性将标准的 CSS 样式应用于代码片段。根据您选择的方法,UI 会有不同的表现效果。

data-select-class="[class name]"

在同一组中,data-select-class 定义了用户可以选择的应用列表。一次只能启用其中的一个

option 属性。

data-toggle-class="[class name]"

data-toggle-class 用于将一个或多个 CSS 类从列表应用到代码片段。多个类选择可以同时应用。

让我们来演示一下默认 options 手机用如何适用于基本模型的示例。

我们首先在视图文件夹中添加一个新文件——options.xml 并添加默认的<Odoo> <xml>标记。创建一

个新的模板复制/粘贴以下内容。

<template id="snippet_testimonial_opt" name="Snippet Testimonial Options" inherit_id="website.snippet_options">

<xpath expr="//div[@data-js='background']" position="after"> <div data-selector=".snippet_testimonial"> <!-- Options group --> <li class="dropdown-submenu"> <a href="#">Your Option</a> <ul class="dropdown-menu"> <!-- Options list --> <li data-select-class="opt_shadow"><a>Shadow Images</a></li> <li data-select-class="opt_grey_bg"><a>Grey Bg</a></li> <li data-select-class=""><a>None</a></li> </ul> </li> </div> </xpath> </template>

上图的模板将继承”website.snippet_options”模板,在选项<xpath expr 属性>之后添加自己的选项。要

以特定的顺序放置您的选项,可以查看网站模块中的 snippet_options(代码片段)模板,并在需要的位置

之前或之后添加您的选项。

如上图,我们将所有选项包装在一个<div>标签中,该<div>标签将对我们的选项进行分组,并将它们

指向正确的选择器(data-selector=".snippet_testimonial")。

为了定义我们的选项,我们将 data-select-class属性应用到 li 元素标签。当用户选择一个选项时,

该属性中包含的类将自动应用于元素。

由于 selectClass 方法避免了多个选择,最后一个“空”选项将重置该代码段到默认值。

添加 options.xml 到__manifest__.py ,接着更新你的主题。

将我们的代码片段放到页面上,您会注意到我们的新选项会自动添加到自定义菜单中。检查页面时,

也可以发现当选择一个选项时,该类将被应用到元素中。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

为了提供目的的视觉效果我们创建了 CSS 规则。打开我们的 style.less 文件并且

如下图添加。

.snippet_testimonial { border: 1px solid #EAEAEA; padding: 20px; } // These lines will add a default style for our snippet. Now let's create our custom rule

s for the options. .snippet_testimonial { border: 1px solid #EAEAEA; padding: 20px; &.opt_shadow img { box-shadow: 0 2px 5px rgba(51, 51, 51, 0.4); } &.opt_grey_bg { border: none; background-color: #EAEAEA; } }

我们成功地为我们的代码片段创建选项。当创建者单击某个选项时,系统将添加 data-select-class 属

性中指定的类。

当发布者单击某个选项时,系统将添加 data-select-class 属性中指定的类。

通过用 data-toggle-class 替换 data-select-class 类,您将能够同时选择更多的类。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

5.8.3 Javascript 选项

如果需要实现简单的样式更改,则 data-select-class 和 data-toggle-class 都可以用。但是,如果

您的代码片段需要更多定制化内容呢?

正如我们前面说过的,data-js 属性可以分配给一个选项组来定义方法。让我们通过在前面创建的选

项组 div 中添加一个 data-js 属性来装饰页面。

<div data-js="snippet_testimonial_options" data-selector=".snippet_testimonial"> [...] </div>

从现在开始,网站构建者每次进入编辑模式时,都会查找一个 snippet_testimonial_options 方法。

让我们更进一步,创建一个 javascript 文件,命名为 tutorial_editor. js 放到 static 文件夹中。复制/粘贴

以下代码

(function() { 'use strict'; var website = odoo.website; website.odoo_website = {}; })();

我们成功地创建了 javascript 编辑器文件。这个文件将包含代码片段在编辑模式中使用的所有

javascript 函数。让我们使用我们之前创建的 snippet_testimonial_options 方法为我们的代码片段创建一

个新函数。

(function() { 'use strict'; var website = odoo.website; website.odoo_website = {}; website.snippet.options.snippet_testimonial_options = website.snippet.Option.extend({ onFocus: function() { alert("On focus!"); } }) })();

您将注意到,我们使用了一个名为 onFocus 的方法来触发我们的函数。网站构建器提供了几个可以用

来触发定制函数的事件。

事件 描述

start 当创建者在编辑会话时第一次选择该代码段时,或者当代码片段被拖放到

页面中,开始触发。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

事件 描述

onFocus 每当用户选择代码片段时,或者代码片段被拖放到页面中,就会触发。

onBlur 当代码片段失去焦点时,就会发生此事件。

onClone 代码片段被复制之后触发。

onRemove 代码片段被删除之后触发。

onBuilt 在以上事件之后,代码段被拖放到一个 drop 区域。当这个事件被触发时,

内容已经插入到页面中。

cleanForSave 在发布者保存页面前出发。

让我们将新的 javascript 文件添加到编辑器列表中。回到 assets.xml,创建了一个新的模板,跟之前一

个模板一样。这一次我们必须继承 assets_editor,而不是 assets_frontend。

<template id="my_js" inherit_id="website.assets_editor" name="My Js"> <xpath expr="script[last()]" position="after"> <script type="text/javascript" src="/theme_tutorial/static/src/js/tutorial_editor.js"

/> </xpath> </template>

更新你的主题

让我们测试新的 javascript function。编辑模式并进入页面。现在您应该看到我们绑定在 onFocus 事件上

的 javascript 警报。如果您关闭它,然后单击您的代码片段的外部,然后再次单击它,事件将再次触发。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

5.9 编辑参考指南

基本上,页面中的所有元素都可以由发布者编辑。此外,一些元素类型和 css类会在编辑时触发特殊的

网站构建器功能。

5.9.1 layout 布局

<section />

任何 section 元素都可以像块元素一样编辑它,发布者可以移动或复制它。它可以设置背景图片和颜

色。Section 是代码片段的基本容器。

.row > .col-md-*

任 何 bootstrap 的 列 都 是 直 接 存 在 于 一 个 为 .row 的 元 素 中 , 浏 览 器 可 自 动 调 整 大 小 。

contenteditable="False"

这个属性将防止对元素及其所有的子元素进行编辑。

contenteditable="True"

将其应用于 contenteditable="False"中的一个元素,使得元素和其子元素可编辑。

<a href=”#” />

在编辑模式下,任何链接都可以编辑和样式化。使用“链接模式”也可以用一个按钮来替换它.

5.9.2 Media 媒体

<span class=”fa” />

图形元素,点击此图标,会打开 pictogram 库来替换图标。还可以使用 CSS 来转换元素。

<img />

一旦被点击,你就可以替换图片。该元素也可以进行转换。

<div class="media_iframe_video" data-src="[your url]" > <div class="css_editable_mode_display"/> <div class="media_iframe_video_size"/> <iframe src="[your url]"/> </div>

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

这个 html 结构浏览器会自动创建一个可编辑的<iframe>元素

5.10 搜索引擎优化(SEO)最好实践

5.10.1 促进内容插入

现代搜索引擎算法越来越关注内容本身,这意味着算法减少了关键词在原文的出现频率所对应的比重,

而更多关注的是内容是否切实与关键字相关。

因为内容对 SEO 非常重要,所以你应该集中精力给发布者合适的工具用于简单地加入它。重要的是,

您的代码片段是用于响应实际内容的,这意味着无论大小,它们都应该符合浏览器的内容。

让我们看一下这个典型代码片段的两列例子,它以两种不同的方式实现。

Bad

使用固定的图像,浏览器将被迫限制文本以遵循布局。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

Good

使用符合列高度的背景图像,浏览器将可以自由地添加内容,而不考虑图像的高度。

5.10.2 页面分割

基本上,页面分割意味着一个页面被分成几个单独的部分,这些部分被搜索引擎作为单独的条目对待。

当您设计页面或代码片段时,您应该确保使用正确的标记,以方便搜索引擎索引。

<article>

指定一个独立的内容块。在它的内部应该是一个独立的内容,并且可以独立实现相应功能。

您可以嵌套元素在<article>之间。在这个情况下,这意味着嵌套的元素和外部元素相关。

<header>

指示包含的内容块的文章标题部分

<section>

<section>是代码默认标记,它指定了块内容的分段。它可以被用来给<article>内容分割。它是不可见

的,可以用头元素(<h1>-<h6>)去定义它。

<hgroup>

用于包装标题部分(<h1> - <h6>).一个很好的例子是例如一篇文章,标题和副标题都在顶部:

<hgroup> <h1>Main Title</h1> <h2>Subheading</h2> </hgroup>

描述你的页面

定义关键字

您应该使用合适的、相关的关键字或者这些关键词的同义词。您可以在每一页的标题栏中使用内置的

“Promote”函数来定义它们。

开源智造咨询有限公司(OSCG) - 企业快速开发平台 Odoo 开发手册

参考:Document Reference版本 1.0

除非盖章,否则打印后为非控制文件

5.10.3 定义一个标题和一个描述

使用“Promote”方法定义。保持简洁的页面标题,并且页面包含主要的关键词。好的标题能引发共

鸣,回应。

描述虽然对搜索引擎的排名不不重要,但对于获取用户的点击量很关键。这是广告宣传的好机会,让

人们能准确地知道该页面是否包含他们正在寻找的信息。所以每个页面的标题和描述都是独一无二的。