├── README.md
├── 11.md
├── 13.md
├── 2.md
├── 16.md
├── 6.md
├── 7.md
├── 21.md
├── 3.md
├── 10.md
├── 23.md
└── 1.md
/README.md:
--------------------------------------------------------------------------------
1 | # Odoo 14开发者指南(第四版)
2 |
3 | > Odoo 12开发者指南请点击[这里](https://github.com/iTranslateX/odoo-cookbook/tree/v12)
4 |
5 | Odoo 14开发者指南(Cookbook)第四版,是一套提供了各类开发场景的完整资源,帮助读者通过Odoo框架构建复杂商业应用。不论你是希望自定义已有模块、新建模块,还是自定义网站或后台web客户端(JS),本书都讲解到了Odoo开发的方方面面。
6 |
7 | 在最新的发行版中,强大的Odoo框架为快速应用开发提供了广泛的功能。这本新的Odoo开发指南有助于读者探索Odoo 14中的新功能并学习如何从零开始使用这些功能开发Odoo应用。读者还将学习到Odoo 14中新的网站概念并一睹Odoo的新web客户端框架OWL(Odoo Web Library的简称)。
8 |
9 | 在完成安装后,就开始通过真实世界中的案例来探索Odoo框架。然后从基础创建一个新的Odoo模块并逐步了解高级的框架概念。读者还将学习到如何修改已有应用,如销售点应用(PoS)。这本书不只限于后端开发,还包含用于新建视图和微件的高级JavaScript章节。接下来你还将学习到网站开发并通过学习性能优化、调试和自动化测试成为一名合格的Odoo开发者。最后,我们会讲解一些高级概念,如多网站、应用内购买(IAP)、Odoo.sh、IoT Box(物联网盒子)以及部署相关知识。
10 |
11 | 使用Odoo CMS的动态构建代码块可以创建出美观的网站;进而学习到一些高级概念,如缓存、预加载和调试;通过新的OWL框架修改后台JavaScript组件和POS;通过远程过程调用(RPC)来连接和访问Odoo中的任意对象;通过Odoo.sh管理、部署并测试Odoo实例;配置IoT Box添加及升级POS硬件,并了解如何实现IAP服务。
12 |
13 | 在本书的最后,读者可以掌握到构建迷人Odoo应用所需的所有知识,并且会对开发的最佳实践了然于胸,这对于使用Odoo框架也会非常有益。
14 |
15 | ## 本书面向的读者
16 |
17 | 本书对于新入门或希望通过Odoo框架开发出高效的商业应用的有经验的Odoo开发者均适用。具备Python和JavaScript的基础知识在学习本书时将获益更多。
18 |
19 | ## 本书的主要内容
20 |
21 | [第一章 安装Odoo开发环境](1.md):讲解如何为Odoo创建开发环境、启动Odoo、创建配置文件以及启用Odoo开发者工具。
22 |
23 | [第二章 管理Odoo服务端实例](2.md):提供安装来自GitHub插件的一些有用贴士以及在实例中组织源代码的讲解。
24 |
25 | [第三章 创建Odoo插件模块](3.md):讲解Odoo插件模块的结构并提供从零开始创建一个简单的Odoo模块的操作指南分解。
26 |
27 | [第四章 应用模型](4.md):聚焦于Odoo模型结构,并讲解所有字段类型及它们的属性。本章还包含通过继承模块来扩展已有数据库结构的相关技巧。
28 |
29 | [第五章 基本服务端开发](5.md):讲解Odoo中执行增删改查(CRUD)操作的各类框架方法。本章还包含继承和扩展已有方法的各种方式。
30 |
31 | [第六章 管理模块数据](6.md):展示如何和模块代码一并进行数据的迁移。还讲解在新发行版中插件修改了数据模型时如何编写迁移脚本。
32 |
33 | [第七章 调试模块](7.md):提供调试Odoo代码的不同策略并介绍了Python调试器。本章包含在开发者模式下运行Odoo的一些技巧。
34 |
35 | [第八章 高级服务端开发技巧](8.md):讲解ORM框架更高级的课题。对于开发向导、SQL视图、安装钩子(hook)、on-change方法等非常有用。本章还讲解了如何在数据库中执行原生SQL查询。
36 |
37 | [第九章 后端视图](9.md):讲解如何为数据模型编写业务视图以及如何在视图中调用服务端方法。涵盖了常用视图(列表、表单和搜索视图),以及一些更为复杂的视图(看板、图形、日历、透视表等)。
38 |
39 | [第十章 权限安全](10.md):讲解如何在Odoo实例中指定谁可以执行什么操作,方式有创建安全组、编写访问控制列表定义在给定模型中每个组可执行的操作,在必要时还可以编写记录级的规则。
40 |
41 | [第十一章 国际化](11.md):展示Odoo中语言翻译的原理。还讲解如何安装多语言及导入/导出所翻译的词语。
42 |
43 | [第十二章 自动化、工作流、Email和打印件](12.md):描绘了Odoo中为记录实现业务流程的不同工具。还展示了如何使用服务端动作和自动化规则来对业务规则进行支持。本章还讲解可生成动态PDF文档的QWeb报告。
44 |
45 | [第十三章 Web服务端开发](13.md):涵盖Odoo web服务端的核心内容。展示了如何创建自定义URL路由来在指定URL上提供数据服务,以及如何对这些URL进行访问控制。
46 |
47 | [第十四章 CMS网站开发](14.md):讲解如何使用Odoo管理网站。还展示了如何创建和修改美观的网页和QWeb模板。本章还包含如何创建带选项的动态网页构建代码块。它包含一些管理 SEO、用户表单、UTM追踪、网站地图和获取访客地理信息的独立小节。本章还强调了Odoo中最新的多站点概念。
48 |
49 | [第十五章 网页客户端开发](15.md):深入到Odoo的JavaScript部分。涵盖了如何创建新字段微件以及对服务端发送RPC调用。还包含如何从零开始创建全新的视图。读者还将学习到如何创建操作向导。
50 |
51 | [第十六章 Odoo Web Library (OWL)](16.md),介绍名为OWL的新客户端框架。涵盖了OWL组件的生命周期。还包含从零创建字段微件的小节。
52 |
53 | [第十七章 Odoo的应用内购买](17.md):涵盖有关Odoo最新的应用内购买(IAP)概念的所有内容。本章中会学习到如何为IAP创建客户端和服务模块。读者还将学习到如何创建IAP账户并从终端用户提取IAP款项。
54 |
55 | [第十八章 自动化测试用例](18.md):包含如何编写和执行自动化测试用例。这包括服务端、客户端测试用例。本章还包含导览测试用例以及对失败的测试用例设置headless Chrome来获取视频。
56 |
57 | [第十九章 使用Odoo.sh管理、部署和测试](19.md):讲解如何通过PaaS平台Odoo.sh来管理、部署和测试Odoo实例。还涉及到如何管理各类实例,如生产、预发布和部署阶段。本章还包含针对Odoo.sh的各种配置选项。
58 |
59 | [第二十章 Odoo中的远程过程调用(RPC)](20.md):涵盖从外部应用连接Odoo实例的不同方式。本章教你如何通过XML-RPC、JSON-RPC和odoorpc库连接Odoo 以及从Odoo实例访问数据。
60 |
61 | [第二十一章 性能优化](21.md):讲解用于获取Odoo中性能提升的不同概念和模式。本章包含预提取、ORM缓存和代码性能测试来监测性能问题的概念。
62 |
63 | [第二十二章 POS(销售点)](22.md):涵盖 POS 应用的自定义。包含对用户界面、添加新动作按钮、修改业务流和扩展客户菜单的自定义。
64 |
65 | [第二十三章 在Odoo中管理Email](23.md):讲解如何在Odoo中管理email和chatter工具。通过配置邮件服务器开始,然后讲解Odoo框架的邮件API。本章还涵盖Jinja2和QWeb邮件模板、表单视图、字段日志和活动的聊天工具。
66 |
67 | [第二十四章 管理IoT盒子](24.md):给出了最新的IoT盒子硬件的重点讲解。本章涵盖如何配置、访问和调试IoT盒子。还包含一个集成IoT盒子到你的自定义插件的示范。
--------------------------------------------------------------------------------
/11.md:
--------------------------------------------------------------------------------
1 | # 第十一章 国际化
2 |
3 | 全书完整目录请见:[Odoo 14开发者指南(Cookbook)第四版](README.md)
4 |
5 | Odoo支持多语言并允许不同的用户根据自己的方便选用不同的语言。这通过Odoo内置的i18n功能实现。通过字符串翻译,Odoo还支持日期的数字格式及时间格式化等。
6 |
7 | 本章中,我们将学习如何在Odoo中启用多语言以及如何在自定义模块中添加翻译文件。在掌握这些新功能时会有助于提升Odoo的用户体验。
8 |
9 | 本章中,我们将讲解如下内容:
10 |
11 | - 安装语言及配置用户首选项
12 | - 配置语言相关设置
13 | - 通过网页客户端在用户界面翻译文本
14 | - 将翻译字符串导出到文件
15 | - 使用`gettext`工具来简化翻译
16 | - 将翻译文件导入到Odoo中
17 | - 对网站修改自定义URL语言代码
18 |
19 | 这里的很多操作可以通过网页客户端用户界面或命令行来完成。在可以使用的地方,我们会讲解两种选项的使用方法。
20 |
21 | ## 安装语言及配置用户首选项
22 |
23 | Odoo已预置了本地化,也就是说它支持多语言和本地化设置,如日期和数字格式。
24 |
25 | 初次安装时,仅能使用默认语言英语。要对用户开放其它语言和地点,需要先安装这些语言。本节中,我们将学习如何设置用户首选项以及如何进行应用。
26 |
27 | ### 如何实现...
28 |
29 | 启用开发者模式并按照如下步骤在Odoo实例中安装新的语言:
30 |
31 | 1. 访问Settings > Translations > Load a Translation菜单项。这里有如下图所示的Add Language链接。点击该链接会打开一个加载语言的对话框:
32 | 
33 | 图11.1 – 通用设置中的语言选项
34 |
35 | 2. 选择想要加载的语言:
36 | 
37 | 图11.2 – 加载语言的对话框
38 |
39 | 3. 点击Add按钮会加载所选中的语言,会打开如下所示的确认对话框:
40 | 
41 | 图11.3 – 显示语言加载的对话框
42 |
43 | 4. 新语言也可以通过命令行来安装。与上一步同等的命令如下:
44 |
45 | ```
46 | $ ./odoo-bin -d mydb --load-language=zh_CN
47 | ```
48 |
49 | 5. 要设置用户使用的语言,访问Settings > Users & Companies > Users,在用户表单的首选项(Preferences)标签中设置语言(Language)字段的值。在同一界面还可以设置用户的时区:
50 | 
51 | 图11.4 – 设置语言的用户表单
52 |
53 | 用户也可以通过Preferences菜单项来进行这些配置。点击网页端窗口右上角的用户名即可。
54 |
55 | ### 运行原理...
56 |
57 | 用户可以有自己的语言和时区首选项。语言设置用于将用户界面的文本翻译为所选的语言并按本地习惯应用浮点和货币字段。
58 |
59 | 在语言可供用户选择之前,它必须通过**Add language**选项进行过安装。可用的语言列表在开发者模式下可通过Settings > Translations > Languages菜单项进行查看。带有active标记的语言是已安装的。
60 |
61 | 每个Odoo插件模块负责其自身的翻译资源,位于i18n子目录中。每种语言的数据应放在 .po文件中。在我们的示例中,西班牙语的翻译文件通过es_ES.po(中文zh_CN.po)数据文件进行加载。
62 |
63 | Odoo还支持基本语言(base language)的概念。例如,如有西班牙语的es.po文件和墨西哥西班牙语的es_MX.po 文件,那么es.po会作为es_MX.po的基本语言。在安装西语的时候,两个数据文件都会被加载,先安装基本语言,再安装指定的语言。因此,具体语言的翻译文件仅需包含针对语言变体部分的字符串,在本例中即为墨西哥西班牙语。
64 |
65 | `i18n`子目录中还应包含 `.pot`文件,提供一个翻译的模板并包含所有的可翻译字符串。本章中*将翻译字符串导出到文件*一节讲解了如何导出可翻译字符串生成该文件。
66 |
67 | 在安装一种语言时,对应的资源从所有已安装的插件模块中载入并在Translated Terms模型中保存。它的数据可以在 Settings > Translations > Application Terms > Translated Terms 菜单项(注意仅在开发者模式下可见)中进行查看(和编辑)。
68 |
69 | 已安装语言的翻译文件还会在新插件模块安装或已有插件模块升级时进行载入。
70 |
71 | ### 扩展知识...
72 |
73 | 通过再次点击语言中的刷新图标即可在无需升级插件模块的情况下重新载入翻译文件。如果升级了翻译文件而又不想经历升级模块(及其所有依赖)的麻烦时可以使用这种方法。
74 |
75 | 如未勾选 Overwrite Existing Terms复选框,仅加载新翻译的字符串。因此,修改的已翻译字符串不会被加载。如果你希望已存在的翻译也被加载则勾选复选框,这样会覆盖当前已加载的翻译。注意这样在有人通过用户界面手动修改翻译时可能会导致潜在问题。
76 |
77 | 存在前面的复选框是因为我们可以通过进入Settings > Translations > Application Terms > Translated Terms菜单项或在调试菜单中使用Technical Translation快捷选项来编辑指定的翻译。以这种方式添加或修改的翻译不会被覆盖,除在重新加载语言时勾选了Overwrite Existing Terms。
78 |
79 | 同时插件模块可以在i18n_extra子文件夹中带有额外的翻译文件。首先, 下载了i18n子目录中的 .po文件。然后Odoo ORM对基本语言下载文件,然后对变种语言进行下载。按照这个顺序,下载i18n_extra子目录中的.po文件,首先会下载基础语言,然后下载语言变种。最后加载的字符串翻译即为当前使用的。
80 |
81 | ## 配置语言相关设置
82 |
83 | 语言和它们的变种(如`es_MX`为墨西哥西班牙语)也提供本地化设置,如日期和数字格式。
84 |
85 | 它们都有相应的默认值,只要用户使用正确的语言,本地化设置就应该是正确的。
86 |
87 | 但是你可能会想要修改语言的设置。比如,你可能会偏向于在用户界面中使用默认的英语,但又希望修改美式英语的默认日期和数字格式匹配自己的要求。
88 |
89 | ### 准备工作
90 |
91 | 我们需要启用开发者模式。如未启用请参照[第一章 安装Odoo开发环境](1.md)*激活Odoo开发者工具*一节进行激活。
92 |
93 | ### 如何实现...
94 |
95 | 按照如下步骤来修改语言的本地化设置:
96 |
97 | 1. 选择Settings > Translations > Languages菜单项来检查所安装的语言和它们的设置。点击所安装的一种语言会打开带有相应设置的表单:
98 | 
99 | 图11.5 – 配置语言设置的表单
100 | 2. 编辑语言设置。修改日期为ISO格式,将日期格式修改为%Y-%m-%d.。要修改数字格式来使用逗号作为十进制分隔符,修改对应的十进制分隔符和千分位分隔符。
101 |
102 | ### 运行原理...
103 |
104 | 在登录并创建新的Odoo用户会话时,用户首选项中勾选了用户语言并在`lang`上下文键中进行了设置。然后使用它来格式化对应的输出 - 源文本翻译为用户语言,日期和数字以语言的当前本地化设置进行格式化。
105 |
106 | ### 扩展知识...
107 |
108 | 服务端进程可修改动作所运行的上下文。例如,要获取根据美式英语格式设置了日期格式的记录集,不依赖当前用户的语言首选项,可以进行如下操作:
109 |
110 | ```
111 | en_records = self.with_context(lang='en_US').search([])
112 | ```
113 |
114 | 更多详情,请参见[第八章 高级服务端开发技巧](8.md)中的*使用变更的上下文调用方法*一节。
115 |
116 | ## 通过网页客户端在用户界面翻译文本
117 |
118 | 进行翻译最简单的方式是使用由网页客户端提供的翻译功能。这些翻译字符串在数据库中存储,稍后可导出为`.po`文件,要么在插件模块中包含,要么之后进行手动导入。
119 |
120 | 文本字段可带有可翻译内容,也就是说它们的值可以依赖于当前用户的语言。我们还将学习如何在这些字段中设置依赖于语言的值。
121 |
122 | ### 准备工作
123 |
124 | 我们需要启用开发者模式。如未启用请参照[第一章 安装Odoo开发环境](1.md)中*激活Odoo开发者工具*一节进行激活。
125 |
126 | ### 如何实现...
127 |
128 | 我们使用用户组作为示例来演示如何通过网页客户端翻译词汇:
129 |
130 | 1. 导航至想要进行翻译的页面。本例我们通过Settings > Users & Companies > Groups菜单项打开用户组的视图。
131 | 2. 在顶部菜单栏中,点击调试菜单图标并选择Technical Translation选项:
132 | 
133 | 图11.6 – 在当前视频中打开翻译选项
134 | 3. 会显示针对该视图的可翻译词语的列表。编辑某行的Translation Value来修改(或添加)其翻译文本。如果要查找某个具体的源字符串,可使用所列出的过滤器来精简出所显示的文本:
135 | 
136 | 图11.7 – 视图的翻译词语
137 | 组名是可翻译字段。我们来将记录值翻译为所安装的其它语言。
138 | 4. 再次导航至User Groups菜单项,在表单视图中打开其中一条组记录,并点击Edit:
139 | 
140 | 图11.8 – 字段值的翻译
141 | 5. 注意在Name字段的最右端有一个特殊的图标。这表示它是一个可翻译字段。点击该图标打开带有各个安装语言的翻译列表。这样我们可以对这些语言设置翻译。
142 |
143 | ### 运行原理...
144 |
145 | 在ir.translation模型的数据表中进行存储。调试菜单下的Technical Translation选项包含对这些词汇的快速访问,与当前所选择的视图保持对应。
146 |
147 | 类似地,带有可翻译内容的模型字段可包含一个访问已安装语言列表的图标并为每种语言设置相应的值。
148 |
149 | 此外,可以使用Translations > Application Terms > Translated Terms菜单项来从Settings顶部菜单访问翻译词汇。这里,可以看到所有针对我们实例的可用词汇。应使用数据过滤器来定位想要找到的词汇。
150 |
151 | ### 扩展知识...
152 |
153 | Translated Terms菜单项的旁边可以看到一个Generate Missing Term选项。选择它会显示一个对话窗口,包含所安装的语言,然后会从已安装的插件模块中提取可翻译字符串,将新增的词句添加到Translated Terms表格中。与本章*将翻译字符串导出到文件*一节异曲同工。
154 |
155 | 在修改一些模型或视图后这会非常实用。这么做会添加新字符串,这样我们就可以对它们进行翻译。
156 |
157 | 它也可用于从默认en_US语言中获取字符串。然后我们可以利用翻译词句来替换原始英语文本为更适合终端用户具体业务的词汇。
158 |
159 | > 📝重要:在主网站语言外的语言中编辑QWeb视图时,会发现只能修改字符串。这是因为在其它语言中,实际上只能通过Odoo的国际化机制向节点的文本内容添加翻译。
160 |
161 | ## 将翻译字符串导出到文件
162 |
163 | 可导出带有或不带有翻译文本所选语言。可在模块中包含i18n数据,或者稍后通过文本编辑器或是专业工具来执行翻译。
164 |
165 | 我们将演示如何使用标准mail模块来进行导出,读者可以修改为自己想要进行导出的模块。
166 |
167 | ### 准备工作
168 |
169 | 我们需要启用开发者模式。如未启用请参照[第一章 安装Odoo开发环境](1.md)中*激活Odoo开发者工具*一节进行激活。
170 |
171 | ### 如何实现...
172 |
173 | 按照如下步骤来导出mail插件模块的翻译词句:
174 |
175 | 1. 在网页客户端用户界面的Settings顶级菜单中选择Translations > Import/Export > Export Translation菜单项。
176 |
177 | 2. 在Export Translations对话框中,选择所要导出翻译的语言、文件格式以及要导出的模块。要导出翻译模板文件,从Language下拉列表中选择New Language (Empty translation template)。推荐使用 .po格式并且一次只导出一个插件模块 - 本例中为Discuss模块(mail是Discuss应用的技术名称):
178 | 图11.9 – 导出翻译词句对话框
179 |
180 | 3. 完成导出后,会显示一个新的窗口,包含一个下载文件的链接以及一些建议。
181 |
182 | 4. 通过Odoo命令行界面导出mail插件模块的翻译模板文件,键入如下命令:
183 |
184 | ```
185 | $ ./odoo-bin -d mydb --i18n-export=mail.po --modules=mail
186 | $ mv mail.po ./addons/mail/i18n/mail.pot
187 | ```
188 |
189 | 5. 若要通过Odoo命令行界面导出某种语言的翻译模板文件 - 本例中为针对西语的es_ES,键入如下命令:
190 |
191 | ```
192 | $ ./odoo-bin -d mydb --i18n-export=es_ES.po --modules=mail --language=es_ES
193 | $ mv es_ES.po ./addons/mail/i18n
194 | ```
195 |
196 | ### 运行原理...
197 |
198 | Export Translation功能完成两件事:从目标模块导出可翻译字符串,在ir.translation模型中添加新字符串,然后通过翻译词句创建一个文件。可通过网页客户端和命令行界面来进行实现。
199 |
200 | 在通过网页导出时,我们可以选择导出空翻译模板,即带有待翻译字符串和空白翻译的文件,或选择导出一种语言,文件中包含待翻译字符串,及所选语言的翻译内容。
201 |
202 | 可以选择的文件格式有CSV, PO和TGZ。TGZ文件格式导出一个包含PO或POT文件的/i18n/目录结构的压缩文件。
203 |
204 | CSV格式可用于通过Excel执行翻译,但在插件模块中所使用的格式是PO文件。它们应放在i18n子目录中。然后在相应的语言安装时自动加载。在导出这些PO文件时,我们应一次仅导出一个模块。PO文件也是一些翻译工具如Poedit所支持的流行格式。
205 |
206 | 翻译还可以直接通过命令行使用--i18nexport选项进行导出。本节展示如何导出模板文件和已翻译语言文件。
207 |
208 | 本节的第4步中,我们导出了一个模板文件。--i18n-export 选项需要传入导出路径和文件名。记住文件的扩展名要求为CSV, PO或TGZ。该选项需要一个-d参数,用于指定所使用的数据库。还需要--modules选项来指定要导出的插件模块。注意这里无需使用--stop-after-init选项,因为导出命令在完成后自动返回命令行。
209 |
210 | 这会导出一个模板文件。Odoo模块导出的模板应放在i18n文件夹中,扩展名为 .pot。在对模块完成导出操作后,我们通常会将导出的PO文件移到模块的i18n目录中,使用名称.pot。
211 |
212 | 在第5步中,还使用到了--language选项。通过它,导出的不是空翻译文件,而是所选语言的已翻译词句。一种情况是使用Technical Translation功能来通过网页客户端用户界面来执行一些翻译,然后将它们一并导出至模块中。
213 |
214 | ### 扩展知识...
215 |
216 | 视图和模型定义中的文本字符串会自动提取供翻译。对于模型,会提取_description属性、字段名(string属性)、帮助文本和选择字段选项以及模型约束(_constraints和_sql_constraints)的用户文本。
217 |
218 | Python或JavaScript代码中需翻译的文本字符串不会进行自动检查,因此代码应标识出这些字符串,通过下划线函数内封装这些字符串。
219 |
220 | 在模块的Python文件中,应确保使用如下语句进行导入:
221 |
222 | ```
223 | from odoo import _
224 | ```
225 |
226 | 然后它在用到要翻译的文本时可以这样使用:
227 |
228 | ```
229 | _('Hello World')
230 | ```
231 |
232 | 对于那些要使用上下文信息的字符串,我们应使用Python的字符串内插,如下所示:
233 |
234 | ```
235 | _('Hello %s') % 'World'
236 | ```
237 |
238 | 注意插值应放在翻译函数的外面。例如,_("Hello %s" % 'World')是错误的。字符串插值最好应为字符串拼接,这样每个界面文本也仅是一个翻译字符串。
239 |
240 | 对Selection字段要保持警惕!如果对字段定义传递了明确的值列表,显示的字符串会自动标记为翻译。而如果传递一个返回值列表的方法,显示字符串必须显式地标记供翻译。
241 |
242 | 对于手动翻译工作,可以使用任何文本编辑器,但使用特别支持PO文件语法的编辑器会减少格式上的错误,让任务变得轻松。这类编辑器如下所示:
243 |
244 | - [POEDIT](https://poedit.net/)
245 | - [Emacs (PO模式)](https://www.gnu.org/software/gettext/manual/html_node/PO-Mode.html):
246 | - [Lokalize](http://i18n.kde.org/tools)
247 | - [Gtranslator](https://wiki.gnome.org/Apps/Gtranslator):
248 |
249 | ## 使用gettext工具来简化翻译
250 |
251 | PO文件格式是gettext国际化和本地化系统的一部分,它在类Unix系统中经常使用。该系统包含简化翻译任务的一些工具。
252 |
253 | 本节演示如何使用这些工具来辅助翻译插件模块。我们要对自定义模块使用它,所以[第三章 创建Odoo插件模块](3.md)中创建的my_library是一个不错的对象。但是,请随意替换为你自己手边的其它自定义模块,将本节中的my_library替换为相应模块即可。
254 |
255 | ### 如何实现...
256 |
257 | 要通过命令行来管理翻译,假定Odoo的安装位置为~/odoo-work/odoo,执行如下步骤:
258 |
259 | 1. 创建一个目标语言如西语翻译词语的汇总。如果我们的汇总文件名为odoo_es.po,应编写如下代码:
260 |
261 | ```
262 | $ cd ~/odoo-work/odoo # 使用你Odoo安装的路径
263 | $ find ./ -name es_ES.po | xargs msgcat --use-first | msgattrib -- translated --no-fuzzy -o ./odoo_es.po
264 | ```
265 |
266 | 2. 通过Odoo命令行界面导出该插件模块的翻译模板文件并将其放在模块中相应位置:
267 |
268 | ```
269 | $ ./odoo-bin -d mydb --i18n-export=my_module.po --modules=my_module
270 | $ mv my_module.po ./addons/my_module/i18n/my_module.pot
271 | ```
272 |
273 | 3. 如果针对目标语言还没有翻译文件,创建一个PO翻译文件,复用我们在汇总中已发现和翻译的词语:
274 |
275 | ```
276 | $ msgmerge --compendium ./odoo_es.po -o ./addons/my_module/i18n/es_ES.po \
277 | /dev/null ./addons/my_module/i18n/my_module.pot
278 | ```
279 |
280 | 4. 如果翻译文件存在,在汇总中添加翻译:
281 |
282 | ```
283 | $ mv ./addons/my_module/i18n/es_ES.po /tmp/my_module_es_old.po
284 | $ msgmerge --compendium ./odoo_es.po -o ./addons/my_module/i18n/es_ES.po \
285 | /tmp/my_module_es_old.po ./addons/my_module/i18n/my_module.pot
286 | $ rm /tmp/my_module_es_old.po
287 | ```
288 |
289 | 5. 使用如下命令查看PO文件中未翻译的词语:
290 |
291 | ```
292 | $ msgattrib --untranslated ./addons/my_module/i18n/es_ES.po
293 | ```
294 |
295 | 6. 使用你喜欢的编辑器完成翻译。
296 |
297 | ### 运行原理...
298 |
299 | 第1步使用gettext工具中的命令来创建所选择语言的翻译汇总(Compendium) - 本例中为西语。通过在Odoo代码中查找所有的es_ES.po文件并将其传递给msgcat命令来进行实现。我们使用 --use-first标记来避免翻译的冲突(在Odoo代码中已存在一些翻译)。结果会传递给msgattrib过滤器。我们使用--translated选项来过滤掉未翻译的词条,使用--no-fuzzy选项来移除模糊翻译。然后我们将结果保存到odoo_es.po中。
300 |
301 | 前一部分的第2步使用--i18n-export选项调用odoo.py。我们需要对命令行指定数据库,虽然在配置文件中已指定过数据库,还有--modules选项,接一个要导出翻译的以逗号分隔的模块列表。
302 |
303 | 在gettext的世界中,模糊翻译是对源字符串通过msgmerge命令(或其它工具)使用邻近匹配所自动创建的。我们需要在汇总中避免使用。
304 |
305 | 第3步通过在汇总中查找到的已有翻译值新建一个翻译文件。msgmerge命令配合--compendium使用来在汇总文件中查找msgid,匹配那些在第2步中生成的翻译模板文件中的内容。结果保存在es_ES.po 文件中。
306 |
307 | 如果插件已有包含希望持久化翻译的.po文件,应对其重命名并将 /dev/null参数替换为该文件。需要有重命名这一步,以避免在输入和输出中使用相同的文件。
308 |
309 | ### 扩展知识...
310 |
311 | 本小节只涉及到GNU gettext工具集中丰富工具的冰山一角。完整的讲解不是本书的范畴。如果读者兴趣,GNU gettext的文档包含大量的有关对PO操作的信息,参见http://www.gnu.org/software/gettext/manual/gettext.html。
312 |
313 | ## 将翻译文件导入到Odoo中
314 |
315 | 加载翻译的常用做法是把PO文件放在模块的i18n子目录内。在插件模块安装或升级时,会加载翻译文件并添加新翻译的字符串。
316 |
317 | 但是,有些情况下我们希望直接导入翻译文件。本节中我们会学习如何加载翻译文件,既可以通过网页客户端也可以通过命令行。
318 |
319 | ### 准备工作
320 |
321 | 我们需要启用开发者模式。如未启用请参照[第一章 安装Odoo开发环境](1.md)中*激活Odoo开发者工具*一节进行激活。我们还会需要一个po翻译文件,例如myfile.po文件,在本节中我们将进行导入操作。
322 |
323 | ### 如何实现...
324 |
325 | 按照如下步骤来导入翻译词句:
326 |
327 | 1. 在网页客户端用户界面中,通过Settings顶部菜单,选择Translations > Import/Export > Import Translation菜单项。
328 |
329 | 2. 在Import Translations对话框中,填写语言名称和代码,并选择要导入的文件。最后,点击Import按钮来执行导入:
330 | 图11.10 – 导入翻译文件对话框
331 |
332 | 3. 要通过Odoo命令行界面导入翻译文件,我们必须将它放至服务端的插件路径中,然后执行导入:
333 |
334 | ```
335 | $ mv myfile.po ./addons/
336 | $ ./odoo.py -d mydb --i18n-import="myfile.po" --lang=es_ES
337 | ```
338 |
339 | ### 运行原理...
340 |
341 | Import Translation接受一个PO或CSV文件,并将翻译字符串导入到ir.translation数据表中。
342 |
343 | 网页客户端功能中要求填写语言名,但它并没有在导入过程中使用到。还有一个重写选项。如果勾选,会强制导入所有的翻译字符串,即便这些字符串已经存在,在这个过程中会进行覆盖。
344 |
345 | 在命令行中,导入可通过--i18n-import选项来实现。必须提供该文件相对插件路径目录的路径,-d和--language (或-l)也是必传的。还可以通过在命令行中添加--i18n-overwrite 选项来实现重写。注意我们在这里没有使用--stop-after-init选项。不需要使用,因为导入动作在完成时会停止服务。
346 |
347 | ## 对网站修改自定义URL语言代码
348 |
349 | Odoo对网站应用也支持多语言。在网中,当前语言标记为语言字符串。本节中,我们学习如何在URL中修改语言代码。
350 |
351 | ### 准备工作
352 |
353 | 在学习本节前,请确保安装了website模块并且对网站启用了多语言。
354 |
355 | ### 如何实现...
356 |
357 | 按照如下步骤来修改语言的URL代码:
358 |
359 | 1. 通过Settings > Translations > Languages菜单项打开语言列表。点击一个已安装语言会打开如下的表单:
360 | 
361 | 图11.11 – 网站语言URL代码
362 | 2. 这里可以看到URL Code字段。设置为所希望的值。不要添加空格或特殊字符。
363 |
364 | 配置完后,就可以对网站测试看结果了。打开首页并修改语言,就会在URL中看到自定义的语言代码。
365 |
366 | ### 运行原理...
367 |
368 | Odoo通过URL路径来识别网站的语言。例如[www.odoo.com/fr_FR](http://www.odoo.com/fr_FR)用于法语,[www.odoo.com/es_ES](http://www.odoo.com/es_ES)用于西班牙语。这里URL中的fr_FR和es_ES部分是语言的ISO代码,在Odoo中用于监测请求的语言。但有时我们希望将语言设置为对用户更友好的方式。这时就可以更新URL Code字段。一旦做了修改,Odoo网站会使用URL Code的值来识别语言。例如,可以将法语的URL Code设置为 fr,那么[www.odoo.com/fr_FR](http://www.odoo.com/fr_FR)就会转化为[www.odoo.com/fr](http://www.odoo.com/fr)。
369 |
370 | > 📝注:在生产环境中修改URL code不会有问题,Odoo会将ISO语言代码的URL跳转至自定义URL。
--------------------------------------------------------------------------------
/13.md:
--------------------------------------------------------------------------------
1 | # 第十三章 Web服务端开发
2 |
3 | 全书完整目录请见:[Odoo 14开发者指南(Cookbook)第四版](README.md)
4 |
5 | 我们将在本章中介绍Odoo网页服务端部分的基础知识。注意本章中所讲解的为基础部分,有关更高阶的功能,请参见[第十四章 CMS网站开发](14.md)。
6 |
7 | 所有的Odoo网页请求都是由Python库[werkzeug](http://werkzeug.pocoo.org)来进行处理的。虽然werkzeug的复杂部分多隐藏在Odoo便捷的封装器中,学习其底层的运行机制也会非常的有帮助。
8 |
9 | 本章中,我们将讲解如下内容:
10 |
11 | - 让路径在网络中可访问
12 | - 限制线上路径的访问
13 | - 使用传递给handler的参数
14 | - 修改已有handler
15 | - 提供对静态资源的访问
16 |
17 | ## 技术准备
18 |
19 | 学习本章要求安装有在线Odoo平台。
20 |
21 | 本章中使用的所有代码可通过GitHub仓库进行下载:https://github.com/alanhou/odoo14-cookbook/tree/main/Chapter13。
22 |
23 | ## 让路径在网络中可访问
24 |
25 | 本节中我们学习如何让http://yourserver/path1/path2这样的URL可由用户访问。这既有可能是一个网页,也有可能是返回供其它程序使用数据的路径。后一种情况中,我们通常会使用JSON格式来接收参数并提供数据。
26 |
27 | ### 准备工作
28 |
29 | 我们将使用library.book 模型,可参见[第四章 应用模型](4.md),因此如果你还没有按该章进行操作,请通过[GitHub仓库](https://github.com/alanhou/odoo14-cookbook/tree/main/Chapter13/00_initial_module)获取相关代码以便能按照本章示例进行操作。
30 |
31 | 我们希望允许任何用户查询完整的图书列表。此外,我们希望通过JSON请求对程序提供同样的信息。
32 |
33 | ### 如何实现...
34 |
35 | 我们需要添加控制器,按惯例放在一个名为controllers的文件夹中:
36 |
37 | 1. 添加带有我们页面HTML内容的controllers/main.py文件,如下:
38 |
39 | ```
40 | from odoo import http
41 | from odoo.http import request
42 | class Main(http.Controller):
43 | @http.route('/my_library/books', type='http', auth='none')
44 | def books(self):
45 | books = request.env['library.book'].sudo().search([])
46 | html_result = ''
47 | for book in books:
48 | html_result += " %s " % book.name
49 | html_result += ' '
50 | return html_result
51 | ```
52 |
53 | 2. 添加一个函数来以JSON格式提供相同的信息,如下例所示:
54 |
55 | ```
56 | @http.route('/my_library/books/json', type='json', auth='none')
57 | def books_json(self):
58 | records = request.env['library.book'].sudo().search([])
59 | return records.read(['name'])
60 | ```
61 |
62 | 3. 添加controllers/__init__.py文件,如下:
63 |
64 | ```
65 | from . import main
66 | ```
67 |
68 | 4. 在my_library/__init__.py文件中导入controllers,如下:
69 |
70 | ```
71 | from . import controllers
72 | ```
73 |
74 | 在重启服务之后,可以在浏览器中访问/my_library/books并获得书名的列表。要进行JSON-RPC的测试,需要构造一个JSON请求。简单的实现方式是通过使用如下命令在命令行中接收输出:
75 |
76 | ```
77 | curl -i -X POST -H "Content-Type: application/json" -d "{}" localhost:8069/my_library/books/json
78 | ```
79 |
80 | 如果此时得到404报错,可能是在实例中有不止一个数据库。这种情况下,Odoo无法决定使用哪个数据库对请求提供服务。
81 |
82 | 使用--db-filter='^yourdatabasename$'参数来强制Odoo使用安装模块所在的具体数据库。现在该路径应该就可以访问了。
83 |
84 | ### 运行原理...
85 |
86 | 这里的两个关键部分是我们的控制器通过odoo.http.Controller获取,并且用于提供内容服务的方法由odoo.http.route进行装饰。继承 odoo.http.Controller以通过Odoo路由系统注册该控制器,与继承odoo.models.Model注册模型的方式相似。同时Controller有一个处理这一注册的元类。
87 |
88 | 通常由插件所处理的路径以插件名开头,以避免名称的冲突。当然,如果你继承一些插件功能,会使用这个插件名。
89 |
90 | #### odoo.http.route
91 |
92 | route装饰器让我们首先告诉Odoo某一方法可通过web访问,第一个参数决定可以访问哪个路径。除了可传递字符串,也可以传递字符串列表,这样相同的函数可以为多个路径提供服务。
93 |
94 | type参数默认为http,决定所提供服务对应的请求类型。严格意义上说,JSON是HTTP,声明第二个函数为type='json'会让事情变得很轻松,因为接下来Odoo会替我们处理类型转换。
95 |
96 | 现在先不用担心auth参数,会在本章的*限制网络可访问路径的访问*一节中进行讲解。
97 |
98 | #### 返回值
99 |
100 | Odoo中函数的返回值可由route装饰器的type参数来决定。对于type='http',我们通常希望传送一些HTML,因此第一个函数只是返回包含 HTML 的字符串。一种代替方案是使用request.make_response(),可控制在响应中发送的headers。因此要表明页面最后更新的时间,可以在books()中的修改最后一行为如下代码:
101 |
102 | ```
103 | return request.make_response(
104 | html_result, headers=[
105 | ('Last-modified', email.utils.formatdate(
106 | (
107 | fields.Datetime.from_string(
108 | request.env['library.book'].sudo()
109 | .search([], order='write_date desc', limit=1)
110 | .write_date) -
111 | datetime.datetime(1970, 1, 1)
112 | ).total_seconds(),
113 | usegmt=True)),
114 | ])
115 | ```
116 |
117 | 代码发送一个Last-modified头及所生成的 HTML,告诉浏览器列表最后一次修改的时间。我们可以从library.book 模型的write_date字段中提取这一信息。
118 |
119 | 为让前面代码段可以运行,我们需要在文件的顶部添加一些导入语句,如下:
120 |
121 | ```
122 | import email
123 | import datetime
124 | from odoo import fields
125 | ```
126 |
127 | 也可以手动创建一个werkzeug的Response对象并返回,但费这番功夫收获甚微。
128 |
129 | > 📝**重要信息**:手动生成HTML对于演示非常好,但在生产环境的代码中则不应这么做。保持使用模板,如我们在[第十五章 网页客户端开发](15.md)中的*创建或更改模板 - QWeb*一节中所演示的,并通过调用request.render()返回。这样我们可以从容地进行本地化并让代码通过将展示层与业务逻辑分离而更优雅。同时,模板为我们提供函数在输出 HTML 之前转义数据。前面的代码会容易遭受跨站脚本攻击(比如用户可能会把脚本标签放到书名中)。
130 |
131 | 对于JSON请求,只需返回希望交给客户端的数据结构,Odoo会做序列化。这时,应限定所返回的数据类型可进行JSON序列化,通常意味着要使用字典、列表、字符串、浮点型和整型。
132 |
133 | #### odoo.http.request
134 |
135 | **request**对象是引用当前处理请求的静态对像,包含所有执行需要的内容。这里最重要的是request.env属性,包含一个与模型中self.env相同的Environment对象。环境与当前用户绑定,在前例中并不存在,因为我们使用了auth='none'。缺少用户也是我们使用sudo()来在示例代码中调用模型方法的原因。
136 |
137 | 如果习惯于web开发,则会倾向进行会话处理,这是绝对正确的。使用OpenERPSession对象(是对werkzeug的Session对象的轻微封装)的request.session,以及用request.session.sid来访问会话ID。存储会话值,只需将request.session作为字典处理,如以下示例代码所示:
138 |
139 | ```
140 | request.session['hello'] = 'world'
141 | request.session.get('hello')
142 | ```
143 |
144 | > 📝**重要信息**:注意在会话中存储数据与使用全局变量是相同的。仅在必要时才使用它。通常对于多请求动作是需要的,如website_sale模块中的结账。
145 |
146 | ### 扩展知识...
147 |
148 | route装饰器可带有其它的参数来进一步自定义其行为。默认允许所有的HTTP方法,并且Odoo将所有传递的参数组装在一起。使用methods参数,我们可以传递一个可接受的方法列表,通常是['GET'] 或['POST']。
149 |
150 | 要允许跨域请求的话(出于安全和隐私考虑,浏览器阻止对脚本所加载域名以外域名的AJAX和其它类型的请求),可设置cors参数为 * 来允许来自所有域名的请求,或设置一个URI来限定请求为来自该URI的请求。如果未设置这个参数,这也是默认情况,即未设置Access-Control-Allow-Origin头,采取浏览器的默认行为。在本例中,我们可能会希望在/my_module/books/json中进行设置,来允许从其它网站拉取的脚本访问图书列表。
151 |
152 | 默认,Odoo通过对每个请求传递token来保护一些类型的请求免受称为跨站请求伪造(CSRF)的攻击。如果想要关闭,设置csrf参数为False,但应注意这通常不是一个好的想法。
153 |
154 | ### 其它内容
155 |
156 | 参见以下各点来了解有关HTTP路由的更多知识:
157 |
158 | - 如果在同一个实例中托管多个Odoo数据库,那么不同的数据库可能运行在不同的域名中。这时我们可以使用--db-filter选项或使用https://github.com/OCA/server-tools的dbfilter_from_header模块,它有助于按域名过滤数据库。在写本书时这个模块还没有迁移到版本14,但在本书出版时应该已经迁移了。
159 | - 要学习如何使用模板来实现模块化,请参见本章中的*修改已有handler*一节。
160 |
161 | ## 限制线上路径的访问
162 |
163 | 我们将在本节中探讨Odoo为路由所提供的三种验证机制。并使用不同的验证机制来定义路由,展示它们之间的不同之处。
164 |
165 | ### 准备工作
166 |
167 | 我们将对前一节的代码继续进行扩展,还会依赖[第四章 应用模型](4.md)中的library.book模型,所以请准备好相关代码再继续下面的学习。
168 |
169 | ### 如何实现...
170 |
171 | 在controllers/main.py中定义handler:
172 |
173 | 1. 添加显示所有图书的路径,如下例所示:
174 |
175 | ```
176 | @http.route('/my_library/all-books', type='http', auth='none')
177 | def all_books(self):
178 | books = request.env['library.book'].sudo().search([])
179 | html_result = ''
180 | for book in books:
181 | html_result += " %s " % book.name
182 | html_result += ' '
183 | return html_result
184 | ```
185 |
186 | 2. 添加一个显示所有图书的路径并表明哪个是由当前用户所著的。参见如下示例代码:
187 |
188 | ```
189 | @http.route('/my_library/all-books/mark-mine', type='http', auth='public')
190 | def all_books_mark_mine(self):
191 | books = request.env['library.book'].sudo().search([])
192 | html_result = ''
193 | for book in books:
194 | if request.env.user.partner_id.id in
195 | book.author_ids.ids:
196 | html_result += " %s " %
197 | book.name
198 | else:
199 | html_result += " %s " % book.name
200 | html_result += ' '
201 | return html_result
202 | ```
203 |
204 | 3. 添加显示当前用户图书的路径,如下:
205 |
206 | ```
207 | @http.route('/my_library/all-books/mine', type='http', auth='user')
208 | def all_books_mine(self):
209 | books = request.env['library.book'].search([
210 | ('author_ids', 'in', request.env.user.partner_id.ids),
211 | ])
212 | html_result = ''
213 | for book in books:
214 | html_result += " %s " % book.name
215 | html_result += ' '
216 | return html_result
217 | ```
218 |
219 | 通过这段代码,/my_library/all-books和/my_library/allbooks/mark-mine路径对未验证用户所显示内容相同,但登录用户会在后一个路径中看到自己的书以粗体显示。对未验证用户/my_library/allbooks/mine路径完全不可访问。如果未验证依然访问该路径,会被重定向到登录页面进行登录。
220 |
221 | ### 运行原理...
222 |
223 | 验证方法之间的不同基本上通过request.env.user的内容可判断到。
224 |
225 | 对于auth='none'哪怕是已验证用户在访问路径时用户记录也是空的。使用这一个验证的场景是所响应的内容对用户不存在依赖,或者是在服务端模块中提供与数据库无关的功能。
226 |
227 | auth='public'的值将未验证用户设置为一个带有XML ID base.public_user的特殊用户,已验证用户设置为用户自己的记录。对于所提供的功能同时针对未验证和已验证用户而已验证用户又具有一些额外的功能时应选择它,前面的代码中已经演示。
228 |
229 | 使用auth='user'来确保仅已验证用户才能访问所提供的内容。通过这个方法,我们可以确保request.env.user指向已有用户。
230 |
231 | ### 扩展知识...
232 |
233 | 验证方法的逻辑位于base插件的 ir.http模型中。不论在路由的auth参数中传递什么值,Odoo搜索该模型中名为**_auth_method_**的函数,这样可以通过继承它并声明处理所选验证方法来进行自定义。
234 |
235 | 作为示例,我们将提供一个名为base_group_user的验证方法,仅针对属于base.group_user组的当前登录用户,如下例所示:
236 |
237 | ```
238 | from odoo import exceptions, http, models
239 | from odoo.http import request
240 | class IrHttp(models.Model):
241 | _inherit = 'ir.http'
242 | def _auth_method_base_group_user(self):
243 | self._auth_method_user()
244 | if not request.env.user.has_group('base.group_user'):
245 | raise exceptions.AccessDenied()
246 | ```
247 |
248 | 现在可以在装饰器中使用auth='base_group_user',并确保运行这个路由handler的用户是该组的成员。使用一点技巧,我们还可以将其扩展为auth='groups(xmlid1,...)',这一实现留作读者练习,可参见GitHub仓库中的示例代码Chapter13/r2_paths_auth/my_library/models/sample_auth_http.py。
249 |
250 | ## 使用传递给handler的参数
251 |
252 | 能够显示内容自然很棒,但能够根据用户输入显示内容则更佳。本节将演示接收输入和做出响应的不同方式。如同前一小节,我们将使用library.book模型。
253 |
254 | ### 如何实现...
255 |
256 | 首先,我们将添加一个接收传统参数图书 ID来显示其详情的路由。然后,我们使用将参数嵌入路径内的方式来实现同样的功能:
257 |
258 | 1. 添加一个接收图书ID参数的路径,如下例所示:
259 |
260 | ```
261 | @http.route('/my_library/book_details', type='http', auth='none')
262 | def book_details(self, book_id):
263 | record = request.env['library.book'].sudo().browse(int(book_id))
264 | return u'%s Authors: %s' % (
265 | record.name,
266 | u', '.join(record.author_ids.mapped('name')) or 'none',
267 | )
268 | ```
269 |
270 | 2. 添加一个我们可以传递图书ID的路径,如下:
271 |
272 | ```
273 | @http.route("/my_library/book_details/", type='http', auth='public')
274 | def book_details_in_path(self, book):
275 | return self.book_details(book.id)
276 | ```
277 |
278 | 如果在浏览器中访问 /my_library/book_details?book_id=1,应该会看到ID为1的图书的详情页。如不存在,会收到一个报错页面。
279 |
280 | > 报错内容:The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
281 |
282 | 第二个handler允许我们访问/my_library/book_details/1并浏览到相同的内容。
283 |
284 | > **译者注:** 请注意原书中为auth='none',这会出现psycopg2.ProgrammingError: can't adapt type 'RequestUID'报错,因为这里我们用到了模型数据,同时也请检查代码是否为最新的稳定版。
285 |
286 | ### 运行原理...
287 |
288 | 默认,Odoo(实际上是werkzeug)合并了GET和POST参数并将它们通过关键词参数传递给handler。因此,仅需声明接收参数book_id的函数,我们以GET(URL中的参数)或POST(通过以是action属性指定handler的
266 | You had rented the
267 | ${object.book_id.name} book on
268 | ${format_date(object.rent_date)}
269 |
270 | The due date of book is ${format_date(object.return_date)}.
272 |
273 |
274 | Best regards,
275 | Librarian
276 | ]]>
277 |
278 |
279 |
280 | ```
281 |
282 | 2. 在声明文件中注册模板文件:
283 |
284 | ```
285 | ...
286 | 'data': [
287 | 'security/groups.xml',
288 | 'security/ir.model.access.csv',
289 | 'views/library_book.xml',
290 | 'views/library_book_categ.xml',
291 | 'views/library_book_rent.xml',
292 | 'data/mail_template.xml'
293 | ],
294 | ...
295 | ```
296 |
297 | 3. 在library.book.rent模型的表单视图中添加Send reminder按钮来发送邮件:
298 |
299 | ```
300 | ...
301 |
306 | ...
307 | ```
308 |
309 | 4. 在library.book.rent模型中添加book_return_reminder()方法:
310 |
311 | ```
312 | ...
313 | def book_return_reminder(self):
314 | template_id = self.env.ref('my_library.book_return_reminder')
315 | self.message_post_with_template(template_id.id)
316 | ```
317 |
318 | 更新my_library模块应用修改。这会在library.book.rent模型的表单视图中添加一个Send reminder按钮。在点击该按钮时,订阅者会收到如下消息:
319 |
320 | 
321 |
322 | 图23.6 – 通过Jinja模块发送Email
323 |
324 | **译者注**:在正确地配置了邮件发送服务器后除聊天器中的消息外还会收到邮件,以上展示邮件的截图。
325 |
326 | 本节中所展示的流程在想要通过邮件向客户发送更新时会很有用。借助Jinja模板,可以动态地发送基于单条记录的邮件。
327 |
328 | ### 运行原理...
329 |
330 | 第1步中,我们使用Jinja创建了一个邮件模板。Jinja模板帮助我们根据记录数据生成动态邮件。邮件模板存储在mail.template模型中。让我们来看一下为创建Jinja邮件模板需要传递的字段列表:
331 |
332 | - name:用于标识指定模板的模板名称。
333 | - email_from:该字段的值是发送邮件所使用的邮箱地址。
334 | - email_to:该字段的值是接收者的邮箱地址。
335 | - email_cc:该字段的值用于发送邮件拷贝的邮箱地址。
336 | - subject:该字段包含邮件的标题。
337 | - model_id:该字段包含对模型的引用。邮件模板会使用这个模型的数据进行渲染。
338 | - body_html:这个字段将包含邮件模板的内容主体。它是一个Jinja模板,因此可以使用变量、循环、条件等。如果想要学习有关Jinja模板的更多知识,可访问http://jinja.pocoo.org/docs/2.10/。通常我们在CDATA标签中包裹内容,这样body中的内容会被看作字符数据而不是标记语言。
339 | - auto_delete:这是一个在邮件发送后用于删除邮件的布尔字段。该字段的默认值为False。
340 | - lang:这个字段用于将邮件模板翻译为其它语言。
341 | - scheduled_date: 该字段用于计划未来的邮件。
342 |
343 | > **📝小贴士:**可以在email_from, email_to, email_cc, subject, scheduled_date和lang字段中使用${}。这有助于我们动态设置值。参见本节的第1步,我们使用了${object.borrower_id.email}来动态设置了email_to字段。
344 |
345 | 如果仔细查看body_html字段的内容,会发现我们使用了${object.borrower_id.name}。这里,该对象是一个library.book.rent模型的记录集。在渲染期间,${object.borrower_id.name}使用借阅者的名字进行替换。类似object,在渲染上下文中传递了一些其它帮助函数和变量。以下是传递给渲染器上下文的帮助函数列表:
346 |
347 | - object:该变量包含模型的记录集,通过model_id字段在模板中进行设置。
348 | - format_date:这是对用于格式化日期时间对象的方法的引用。
349 | - format_datetime: 这是一个用于转化UTC日期时间为基它时区的日期时间方法的引用。
350 | - format_amount:这是一个对通过货币符号将浮点型转化为字符串的方法的引用。
351 | - format_duration: 该方法用于将浮点值转化为时间,如将1.5转化为01:30。
352 | - user:这是当前用户的记录集。
353 | - ctx:包含环境上下文字典。
354 |
355 | > 📝**注**:如果想要查看模板列表,开启开发者模式,打开Settings > Technical > Email > Templates菜单。模板的表单视图还提供预览渲染后模板的按钮。
356 |
357 | 第2步中,我们在声明文件中注册了模板文件。
358 |
359 | 第3步中,我们在表单视图中添加了一个按钮来调用book_return_reminder()方法,它会向关注者发送邮件。
360 |
361 | 第4步中,我们添加了book_return_reminder()方法,通过点击按钮进行调用。message_post_with_template()方法用于发送邮件。message_post_with_template()方法在模型中通过mail.thread来继承。要发送该邮件,只需要传递模板ID来作为参数。
362 |
363 | ### 扩展知识...
364 |
365 | message_post_with_template()方法用于通过Jinja模板发送邮件。如果只是想要通过普通文本发送邮件,可以使用message_post()方法:
366 |
367 | ```
368 | self.message_post(body="Please return your book on time")
369 | ```
370 |
371 | 以上的代码会在聊天器中添加一条Please return your book on time 的消息。所有的关注者会收到这条通知消息。如果只是想要记下这条消息,通过subtype_id参数调用该方法。
372 |
373 | ## 使用QWeb模板发送邮件
374 |
375 | 在前一节中,我们学习了如何使用Jinja模板发送邮件。本节中,我们了解发送动态邮件的另一种方式。我们将借助于QWeb模板来发送邮件。
376 |
377 | ### 准备工作
378 |
379 | 本节,我们将使用前一节*使用Jinja模板发送邮件*中的my_library模块。我们将使用QWeb模板来向借阅者发送邮件来告诉他/她们图书已逾期。
380 |
381 | ### 如何实现...
382 |
383 | 按照如下步骤来向借阅者发送提醒邮件:
384 |
385 | 1. 在my_library/data/mail_template.xml文件中添加QWeb模板:
386 |
387 | ```
388 |
389 | Dear ,
390 | You had rented the
391 |
392 |
393 | book on
394 |
395 | The due date of book is
396 |
397 |
398 |
399 |
400 |
401 |
402 | Best regards,
403 |
404 | Librarian
405 |
406 |
407 | ```
408 |
409 | 2. 在library.book.rent模型的表单视图中添加Send reminder (QWeb) 按钮来发送邮件:
410 |
411 | ```
412 | ...
413 |
414 |
415 |
416 |
417 |
418 |
419 | ...
420 | ```
421 |
422 | 3. 在library.book.rent模型中添加book_return_reminder_qweb()方法:
423 |
424 | ```
425 | ...
426 | def book_return_reminder_qweb(self):
427 | self.message_post_with_view('my_library.book_return_reminder_qweb')
428 | ```
429 |
430 | 4. 更新my_library模块来应用修改。这会在library.book.rent模型的表单视图中添加一个Send reminder (QWeb)按钮。在点击该按钮时,关注者会收到一条如下的消息:
431 | 图23.7 –通过QWeb模板发送的邮件
432 |
433 | 本节中所展示的流程和前一节*使用Jinja模板发送邮件*基本一致。唯一的不同是模板类型,因为本节中使用QWeb模板。
434 |
435 | **译者注**:QWeb 模板的内容通过后台Settings > Technical > User Interface > Views 进行查看
436 |
437 | ### 运行原理...
438 |
439 | 第1步中,我们通过book_return_reminder_qweb ID创建了一个QWeb模板。如果查看这个模板,我们不再使用 format_date()数据字段方法。这是因为QWeb渲染引擎自动根据用户的语言处理并展示日期。出于同样的原因,你不需要使用format_amount() 方法来展示货币符号。QWeb渲染引擎将会自动对其进行管理。如果想要了解更多有关QWeb模板的知识,可参见[第十四章 CMS网站开发](14.md)中的*创建或更改模板 - QWeb*一节。
440 |
441 | 在第2步中,我们在表单视图中添加了一个按钮来调用book_return_reminder_qweb()方法,它向关注者发送邮件。
442 |
443 | 在第3步中,我们添加了book_return_reminder_qweb()方法,它通过按钮的点击进行调用。message_post_with_view()方法用于发送邮件。message_post_with_view()方法在模型中通过mail.thread来继承。要发送邮件,只需传递网页模板的XML ID来作为参数。
444 |
445 | 通过QWeb模板发送邮件与前一节的运行方式相同,但QWeb邮件模板和Jinja邮件模板之间有一些细微的差别。以下是对这两种模板的快速对比:
446 |
447 | - 在邮件模板中没有简单的发送额外参数的方式。需要在对象变量中使用记录集来获取动态数据。另一方面,QWeb邮件模板可以通过values参数在渲染器上下文中传递额外的值:
448 |
449 | ```
450 | self.message_post_with_view(
451 | 'my_library.book_return_reminder_qweb',
452 | values={'extra_data': 'test'}
453 | )
454 | ```
455 |
456 | - 要管理日期格式、时区和带有货币符号的金额,在Jinja模板中需要使用format_date、format_tz和format_amount函数,而在QWeb模板中则是自动进行管理的。
457 |
458 | - 在Jinja中不能修改其它模块的已有模板,但在QWeb模板中可以通过继承修改邮件模板。如果想要了解更多有关QWeb继承的知识,请参见[第十四章 CMS网站开发](14.md)中的*创建或更改模板 - QWeb*一节。
459 |
460 | - 可以直接通过消息编辑器选择并使用Jinja模板。在下图中,右下角的下拉菜单用于选择一个Jinja模板:
461 | 
462 | 图23.8 – 模板选项
463 |
464 | - 使用QWeb,并不能直接通过消息编辑器来选择模板。
465 |
466 | ### 扩展知识...
467 |
468 | 所有的方法(message_post, message_post_with_template和message_post_with_view)都和用户首选项有关。如果用户通过用户首选项修改通知管理选项,该用户将不会收到邮件:取而代之的是他们将在Odoo的用户界面中接收到通知。对于客户同样如此,如果用户取消邮件订阅,他们将不会通过邮件收到任何更新。
469 |
470 | 此外,Odoo的消息线程遵循一种称为子类型(subtypes)的概念。子类型仅用于接收你所感兴趣的邮件。可以在message_post_*方法中传递额外的参数subtype_id,根据子类型发送邮件。通常,用户会通过Follow按钮的下拉选项管理它们的子类型。我们假定用户设置他们的子类型如下:
471 |
472 | 
473 |
474 | 图23.9 – 编辑子类型的选项
475 |
476 | 根据用户的首选项,该用户仅会收到讨论消息的邮件。
477 |
478 | ## 管理邮件别名(待手动验证)
479 |
480 | 邮件别名是Odoo中用于通过接收邮件创建记录的一种功能。邮件别名的最简单示例是销售团队。只需要发送一封邮件到sale@yourdomain.com,Odoo会在销售团队中针对crm.lead 新建一条记录。本节中,我们将创建一个邮件别名来创建一本图书的借阅记录。
481 |
482 | ### 准备工作
483 |
484 | 本节我们将使用前一节*使用QWeb模板发送邮件*中的my_library模块。我们会使用rent@yourdomain.com邮箱地址创建自己的邮件别名。如果向这个邮箱发送一封标题中带有图书名称的邮件,在 library.book.rent模型中就会创建一条记录。
485 |
486 | ### 如何实现...
487 |
488 | 按照如下步骤来为library.book.rent模型添加邮件别名:
489 |
490 | 1. 在my_library/data/mail_template.xml文件中添加邮件别名数据:
491 |
492 | ```
493 |
494 | rent
495 |
496 |
497 | partners
498 |
499 | ```
500 |
501 | 2. 在my_library/models/library_book_rent.py文件中添加如下导入:
502 |
503 | ```
504 | import re
505 | from odoo.tools import email_split, email_escape_char
506 | ```
507 |
508 | 3. 在library.book.rent模型中重载message_new()方法:
509 |
510 | ```
511 | @api.model
512 | def message_new(self, msg_dict, custom_values=None):
513 | self = self.with_context(default_user_id=False)
514 | if custom_values is None:
515 | custom_values = {}
516 | regex = re.compile("^\[(.*)\]")
517 | match = regex.match(msg_dict.get('subject')).group(1)
518 | book_id = self.env['library.book'].search([
519 | ('name', '=', match),
520 | ('state', '=', 'available')], limit=1)
521 | custom_values['book_id'] = book_id.id
522 | email_from = email_escape_char(email_split(msg_dict.get('from'))[0])
523 | custom_values['borrower_id'] = self._search_on_partner(email_from)
524 | return super(LibraryBookRent, self).message_new(msg_dict, custom_values)
525 | ```
526 |
527 | 更新my_library模块来应用修改。然后发送邮件到rent@yourdomain.com。确保在邮件标题中包含了图书名称,如[Odoo 14 Development Cookbook] Request to borrow this book。这会创建一条新的library.book.rent记录并且它会显示如下:
528 |
529 | 
530 |
531 | 图23.10 – 通过邮件生成的记录
532 |
533 | 在向rent@yourdomain.com发送带有图书名邮件标题的邮件时,Odoo会生成一条新借阅记录。注意在仅对图书在图书馆中可借阅时才会生效。
534 |
535 | ### 运行原理...
536 |
537 | 第1步中,我们创建了mail.alias记录。这个别名会处理rent@yourdomain.com邮箱地址。在向这个地址发送邮件时,Odoo会在library.book.rent 模型中新建一条记录。如果想要查看系统中活跃的别名列表,可打开Setting > Technical > Email > Aliases。以下为配置别名的可用字段列表:
538 |
539 | - alias_name:该字段保存邮件地址的前缀部分,例如,rent@yourdomain.com中的rent是邮箱地址的前缀部分。
540 | - alias_model_id:对接收邮件应创建记录的模型的引用。
541 | - alias_user_id:在收到邮件时,会通过该字段中的用户环境来创建记录。
542 | - alias_contact:这个字段保存该别名的权限首选项。可用的选项包含所有人, 成员, 关注者和雇员。
543 | - alias_defaults:在收到邮件时,它的记录会在针对具体别名的模型中创建。如果想要在记录中设置默认值,在这个字段中以字典的形式提供值。
544 |
545 | 第2步中,我们添加了所需的导入。第3步中,我们重载了message_new()方法。该方法在别名邮箱上接收到新邮件时自动被调用。这个方法会接收两个参数:
546 |
547 | - msg_dict:这个参数是包含有关接收邮件信息的字典。它包含邮件信息如发件人邮箱地址、收件人邮箱地址、邮件标题和邮件内容。
548 | - custom_values:这是一个用于新建记录的自定义值。与在别名记录中使用alias_defaults字段设置的值相同。
549 |
550 | 本节中我们重载了message_new()方法并从邮件标题中通过一个正则表达式获取到图书的标题。然后借助在第2步中导入的工具获取到发件人的邮箱。我们使用发件邮箱地址来查找借阅者记录。然后,我们通过这两个值来更新custom_values:books_id和borrower_id。我们将更新后的custom_values数据传递给super()方法,它会通过给定的books_id和borrower_id新建一条library.book.rent记录。这就是在发送邮件到别名时创建记录的方法。
551 |
552 | 注意本节在没有发送正确的邮件标题时会产生报错,如[book name] remaining subject。读者可以根据自己的业务逻辑来更新程序以避免报错。
553 |
554 | ### 扩展知识...
555 |
556 | 一些业务模型要求你需要对每条记录使用单独的别名。例如,销售团队模块对每个团队有独立的别名,如对印度团队使用sale-in@example.com,对比利时团队使用sale-be@example.com。如果想要在自己的模型中管理这种别名,可以使用mail.alias.mixin。为了能在自己的模型中使用它,需要继承这个mixin:
557 |
558 | ```
559 | class Team(models.Model):
560 | _name = 'crm.team'
561 | _inherit = ['mail.alias.mixin', 'mail.thread']
562 | ```
563 |
564 | 在继承该mixin后,你将需要向表单视图添加alias_name字段,以便终端用户可以自己添加别名。
565 |
566 | ## 在聊天器中记录用户修改
567 |
568 | Odoo框架提供了一种内置工具在聊天器中记录字段修改。本节中,我们将对其中的一些字段启用登记,因此如果对它们做出修改,Odoo会在聊天器中添加登记。
569 |
570 | ### 准备工作
571 |
572 | 本节我们使用前一节*管理邮件别名*中的my_library模块。本节中我们将记录 library.book 模型中一些字段的修改。
573 |
574 | ### 如何实现...
575 |
576 | 修改这些字段的定义,在修改它们时启用对这些字段的记录。如以下代码片断所示:
577 |
578 | ```
579 | class LibraryBookRent(models.Model):
580 | _name = 'library.book.rent'
581 | _inherit = ['mail.thread', 'mail.activity.mixin']
582 | book_id = fields.Many2one('library.book', 'Book', required=True)
583 | borrower_id = fields.Many2one('res.partner', 'Borrower', required=True)
584 | state = fields.Selection([('ongoing', 'Ongoing'),
585 | ('returned', 'Returned')],
586 | 'State', default='ongoing',
587 | required=True,
588 | tracking=True)
589 | rent_date = fields.Date(default=fields.Date.today,
590 | tracking=True)
591 | return_date = fields.Date(tracking=True)
592 | ```
593 |
594 | 更新my_library模块来应用修改。在library.book.rent模型中新建一条记录,在这些字段中做一些修改,然后归还图书。如果查看聊天器,会看到如下日志:
595 |
596 | 
597 |
598 | 图23.11 – 聊天器中的修改日志
599 |
600 | 在对state, rent_date或return_date作出修改时,都会在聊天器中看到新的日志。这会有助于我们查看该记录的完整历史信息。
601 |
602 | ### 运行原理...
603 |
604 | 通过对这个字段添加tracking=True属性,可以对该字段启用登记。在设置tracking=True属性时,Odoo会在聊天器中添加一条更新字段值时的修改记录。如果对多对记录启用追踪,又希望对追踪记录进行排序,可以在追踪参数中这样传递数值:tracking=20。在传递tracking=True时,使用的是默认排序值,即100.
605 |
606 | 本节中,我们对state, rent_date和return_date字段添加了tracking=True,这表示Odoo会在更新rent_date, return_date或state 字段的值时记录这些修改。查看*如何实现*小节中的截图,我们仅修改了rent_date和return_date字段。
607 |
608 | 注意tracking功能仅用于继承了 mail.thread模型的模型,因为代码相关的聊天器和日志是mail.thread 模型的一部分。
609 |
610 | ## 定期发送摘要邮件
611 |
612 | Odoo框架支持定期发送摘要邮件。通过摘要邮件,可以发送KPI业务信息的邮件。本节中,我们向图书管理员发送借书相关的数据。
613 |
614 | ### 准备工作
615 |
616 | 本节我们使用前一节*在聊天器中记录用户修改*中的my_library模块。
617 |
618 | ### 如何实现...
619 |
620 | 按照如下步骤生成借书记录的摘要邮件:
621 |
622 | 1. 继承digest.digest模型,添加用于KPI的字段:
623 |
624 | ```
625 | class Digest(models.Model):
626 | _inherit = 'digest.digest'
627 |
628 | kpi_book_rent = fields.Boolean('Book Rent')
629 | kpi_book_rent_value = fields.Integer(compute='_compute_kpi_book_rent_value')
630 |
631 | def _compute_kpi_book_rent_value(self):
632 | for record in self:
633 | start, end, company = record._get_kpi_compute_parameters()
634 | record.kpi_book_rent_value = self.env['library.book.rent'].search_count([
635 | ('create_date', '>=', start),
636 | ('create_date', '<', end)
637 | ])
638 | ```
639 |
640 | 2. 继承digest.digest模型的表单视图并添加KPI字段:
641 |
642 | ```
643 |
644 |
645 |
646 | digest.digest.view.form.inherit.library
647 | digest.digest
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 | ```
659 |
660 | 更新模块应用修改。在更新模块后,打开Settings > Technical > Emails > Digest Emails,如下图所示:
661 |
662 | 
663 |
664 | 图23.12 – 开启借书数据的摘要邮件
665 |
666 | **译者注:**除在models/__init__.py 中添加新增的模型外,还应在声明文件中添加对 digest 模块的依赖以及添加新增的视图文件。
667 |
668 | 在开启之后,如果进行了订阅,就会开始接收到这些摘要邮件。
669 |
670 | ### 运行原理...
671 |
672 | 需要两个字段来构建自定义摘要邮件。第一个字段为布尔字段,用于启用及禁用KPI,第二个字段为计算字段,在请求KPI值时进行调用。第1步中我们创建了这两个字段。如果查看compute字段的字义,会发现使用了_get_kpi_compute_parameters方法。这一方法返回三个参数:开始日期、结束日期和公司记录。可以使用这些参数来生成KPI的值。如们返回指定日期范围内借书的数量。如果KPI兼容了多站点,那么就可以使用公司参数。
673 |
674 | 第2步中,我们在摘要表单视图中添加了一个字段。该字段用于启用/禁用摘要邮件。启用后会开始收到如下的摘要邮件:
675 |
676 | 
677 |
678 | 图23.13 – 借书记录的摘要邮件
679 |
680 | 开启开发者模式,然后打开Settings > Technical > Emails > Digest Emails。此外可以配置摘要邮件的接收人以及设置摘要邮件的发送频率。也可以在这里启用/禁用摘要邮件。
681 |
682 | **译者注**:以上通过设置公司的邮箱进行的发送,根据配置的不同所使用的邮箱也会不同。受限于国内企业邮箱的限制,可能需要添加一个postmaster-odoo@yourdomain.com 的邮箱。
683 |
--------------------------------------------------------------------------------
/1.md:
--------------------------------------------------------------------------------
1 | # 第一章 安装Odoo开发环境
2 |
3 | 全书完整目录请见:[Odoo 14开发者指南(Cookbook)第四版](README.md)
4 |
5 | 配置Odoo开发环境有很多种方式。本章中提供了其中的一种,你肯定可以在网上找到其它的教程讲解其它方法。请记住本章中所讲解的是开发环境,与生产环境的要求是不同的。
6 |
7 | 如果你是一个Odoo开发新手,必须要了解Odoo生态的方方面面。第一部分会给出这些方面的简短介绍,然后我们就会进入到Odoo开发所需的安装。
8 |
9 | 本章中,我们将讲解如下主题:
10 |
11 | - 了解Odoo生态系统
12 | - 源码轻松安装Odoo
13 | - 管理Odoo服务端数据库
14 | - 在文件中存储实例配置
15 | - 启用Odoo开发者工具
16 | - 更新插件模块列表
17 |
18 | 参考安装脚本:[Github](https://github.com/alanhou/odoo14-cookbook/tree/main/Chapter01)
19 |
20 | ## 了解Odoo生态系统
21 |
22 | Odoo为开发者提供了开箱即用的模块结构。它强大的框架有助于开发者很快地构建项目。在开启成为成功的Odoo开发者之旅以前,应该要熟悉Odoo生态中的一些特性。
23 |
24 | ### Odoo版本
25 |
26 | Odoo有两个版本。第一个是社区版,完全开源,另一个是企业版,需要支付授权证书费用。不同于其它软件供应商,Odoo企业版仅仅是在社区版基础上添加了一些附加特性或新应用的高级应用。基本上,企业版运行于社区版之上。社区版采用Lesser General Public License v3.0 (LGPLv3)许可证书,并带有企业版企业资源计划(ERP)的所有基础应用,如销售、客户关系管理(CRM)、发票、采购、网站构建器等等。而企业版采用 Odoo 企业版许可证书,这是一个自有证书。Odoo 企业版带有很多高级功能如完整的财务、studio、基于IP的语音传输(VoIP)、移动端响应式设计、电子签名、营销自动化、快递与银行的集成以及IoT等。企业版还为你提供无限的漏洞修复支持。下图显示了企业版依赖于社区版,这也是为什么使用企业版时需要用到社区版:
27 |
28 | [](http://alanhou.org/homepage/wp-content/uploads/2019/05/2019050403383533.png)
29 |
30 |
31 |
32 | 图1.1 – 社区版和企业版的差别
33 |
34 | 读者可以访问https://www.odoo.com/page/editions查看这两个版本的完整对比。
35 |
36 | > 📝Odoo有数量庞大的社区开发人员,这也是你在应用商店中看到有大量的第三方应用(模块)的原因。有些免费应用使用Affero General Public License version 3 (AGPLv3)许可证书。如果你的应用依赖于这些应用就不能使用其自有证书。Odoo自有证书的应用仅能在拥有LGPL或其它自有证书的模块基础上进行开发。
37 |
38 | ### Git仓库
39 |
40 | Odoo的完整代码托管在GitHub上。可以在这里对稳定版提交漏洞/问题。还可以通过提交拉取请求(Pull Request - PR)来提议添加新功能。Odoo有许多个仓库,参见下表来获取更多信息:
41 |
42 | | 仓库 | 用途 |
43 | | :--------------------------------- | :---------------------------------------------- |
44 | | https://github.com/odoo/odoo | 这是 Odoo 的社区版。对公众开放。 |
45 | | https://github.com/odoo/enterprise | 这是 Odoo 的企业版。仅对Odoo 官方合作伙伴开放。 |
46 | | https://github.com/odoo-dev/odoo | 这是不断开发中的仓库。对公众开放。(已废弃) |
47 |
48 | 每年,Odoo会发布一个大版本(长期支持(LTS)版本)和数个小版本。小版本多用于Odoo的在线SaaS服务,也就是说Odoo的SaaS用户可以更早地使用到这些功能。GitHub 上大版本分支的名称像14.0, 13.0和12.0,而小版本分支名称有saas-14.1和saas-14.2。小版本多用于Odoo 的SaaS平台。master分支处于开发中,不稳定,因此,不建议在生产环境中使用它,因为它可能导致数据库的崩溃。
49 |
50 | ### Runbot
51 |
52 | Runbot是Odoo的自动化测试环境。在Odoo的Github 分支中有新提交时,Runbot会拉取最新的修改并并创建最近4个提交的构建。这里,你可以测试所有的稳定版和开发中的分支。甚至可以使用到企业版并测试它的开发分支。
53 |
54 | 每个构建有不同的背景色,表明测试用例的状态。绿色背景表示所有的测试用例成功运行,用户可以测试该分支,而红色背景表示在这个分支上有些测试用例出错了,有些功能在该构建上可能出现崩溃。可以查看到所有测试用例的日志,会显示在安装过程中所发生的具体问题。每个构建有两个数据库。数据库all安装了所有的模块,而数据库base仅安装了Odoo的基础模块。每个构建均安装了基本演示数据,因此可以快速进行测试而无需额外的配置。
55 |
56 | > ℹ️使用如下 URL 来访问runbot:http://runbot.odoo.com/runbot
57 |
58 | 以下账户信息可用于访问任一runbot构建:
59 |
60 | - 登录ID: admin 密码: admin
61 | - 登录ID:demo 密码: demo
62 | - 登录ID: portal 密码: portal
63 |
64 | > 📝这是公共测试环境,因此有时可能会有其它用户使用/测试你所测试的相同分支。
65 |
66 | ### Odoo应用商店
67 |
68 | Odoo在几年前发布了应用商店,当即大热。现在,那里托管着22,000多个不同的应用。在应用商店中,可以找到大量的针对不同版本的免费和付费应用。包含不同垂直业务领域的具体解决方案,如教育、食品行业和医药业。它还包含一些继承了已有Odoo应用或添加了新功能的应用。应用商店还为Odoo网站构建器提供了大量的美观的主题。在[第三章 创建Odoo插件模块](3.md)中,我们将学习如何为你的自定义模块设置价格和币种。
69 |
70 | 可以通过如下链接访问Odoo应用商店:https://www.odoo.com/apps。
71 |
72 | > 📝Odoo开源了很版本13和14的主题。在此前的版本均为付费主题。也就是在 Odoo 13和14中无需花费额外费用即可下载、使用这些漂亮的主题。
73 |
74 | ### Odoo社区联盟(OCA)
75 |
76 | Odoo社区联盟(OCA)是一个开发/管理Odoo社区模块的非营利组织。所有的OCA模块都开源并由Odoo社区成员维护。在OCA的GitHub账户下,可以找到针对不同Odoo应用的多个仓库。除Odoo模块外,它还包含很多工具、迁移库、会计本地化等等。
77 |
78 | 以下是OCA官方GitHub账号的URL:https://github.com/OCA。
79 |
80 | ### Odoo官方帮助论坛
81 |
82 | Odoo 拥有一个非常强大的框架,大量的操作只需通过使用/激活选项或遵循指定的模式即可实现。因此,如果你碰到了一些技术问题或是对一些复杂用例不确定,那么就可以在Odoo官方帮助论坛上询问。这个论坛上有大量活跃的开发人员,包含一些Odoo官方的员工。
83 |
84 | 在如下 URL 上可以搜索或提交你的问题:https://help.odoo.com/。
85 |
86 | ### Odoo的eLearning平台
87 |
88 | 最近Odoo发布了一个全新的eLearning平台。该平台上有大量的视频讲解如何使用各类Odoo应用。在编写本书时,平台上还没有技术相关视频,只是一些介绍功能的视频。
89 |
90 | eLearning平台的网址为:https://www.odoo.com/slides。
91 |
92 | ## 通过源码轻松安装Odoo
93 |
94 | 强烈推荐使Linux Ubuntu操作系统来安装Odoo,因为这是Odoo测试、调试和安装Odoo企业版所使用的操作系统,此外大部分的Odoo开发人员都使用GNU/Linux,使用GNU/Linux而非Windows或macOS会更有机会获取操作系统相关问题的社区支持。
95 |
96 | 也推荐使用和生产环境相同的环境(相同发行版和版本号)进行开发。这样可以避免讨厌的“彩蛋”,比如在部署当天发现有个库有一个预料外的版本,会有一些不同和不兼容的问题。如果你的工作站使用不同的操作系统,一种推荐的方式是在工作站上配置虚拟机(VM)并在 VM 上安装 GNU/Linux发行版。
97 |
98 | > 📝Ubuntu已成为微软商店中的一个应用,如果不希望切换操作系统的话也可以使用它。
99 |
100 | 本书假定读者使用的是Ubuntu 18.04 LTS版,但也可以使用其它的GNU/Linux系统。不论你选择哪个Linux发行版,都应该要有一个从命令行使用它的概念,有系统运维相关知识显然会更好。
101 |
102 | 相关文章:[Odoo 14安装简明教程(CentOS)](https://alanhou.org/odoo14-installation/)
103 |
104 | ### 准备工作
105 |
106 | 假设读者已运行了Ubuntu 18.04并已拥有 root 密码或配置了sudo拥有root访问权限。下面的部分中,我们将安装Odoo的相关依赖并通过GitHub下载Odoo源码。
107 |
108 | > ℹ️有些配置需要使用到系统登录用户,这里我们在需要用到用户名时在命令行中使用$(whoami)。这个shell命令可以在所键入命令中替换为登录用户。
109 |
110 | 如果你有GitHub账户的话有些操作一定会更容易。如果还没有GitHub账户,请访问https://github.com并创建账户。
111 |
112 | ### 如何安装...
113 |
114 | 使用源码安装 Odoo,需要按照如下的步骤:
115 |
116 | 1. 运行如下命令来安装主要依赖:
117 |
118 | ```
119 | $ sudo apt-get update
120 | $ sudo apt install git python3-pip build-essential wget python3-dev python3-venv python3-wheel libxslt-dev libzip-dev libldap2-dev libsasl2-dev python3-setuptools -y
121 | ```
122 |
123 | 2. 下载并安装wkhtmltopdf:
124 |
125 | ```
126 | $ wget https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.5/wkhtmltox_0.12.5-1.bionic_amd64.deb
127 | $ sudo dpkg -i wkhtmltox_0.12.5-1.bionic_amd64.deb
128 | ```
129 |
130 | 如果以上命令出现了报错,通过如下命令可强制安装依赖:
131 |
132 | ```
133 | $ sudo apt-get install -f
134 | ```
135 |
136 | 3. 此时安装PostgreSQL数据库
137 |
138 | ```
139 | $ sudo apt install postgresql -y
140 | ```
141 |
142 | 4. 配置PostgreSQL
143 |
144 | ```
145 | $ sudo -u postgres createuser --superuser $(whoami)
146 | ```
147 |
148 | 5. 配置git(以下信息请自行修改):
149 |
150 | ```
151 | $ git config --global user.name "Your Name"
152 | $ git config --global user.email youremail@example.com
153 | ```
154 |
155 | 6. 克隆 Odoo 基础代码:
156 |
157 | ```
158 | $ mkdir ~/odoo-dev
159 | $ cd ~/odoo-dev
160 | $ git clone -b 14.0 --single-branch --depth 1 https://github.com/odoo/odoo.git
161 | ```
162 |
163 | 7. 创建一个odoo-14.0 虚拟环境并启用:
164 |
165 | ```
166 | $ python3 -m venv ~/venv-odoo-14.0
167 | $ source ~/venv-odoo-14.0/bin/activate
168 | ```
169 |
170 | 8. 在venv中安装Odoo的Python依赖:
171 |
172 | ```
173 | $ cd ~/odoo-dev/odoo/
174 | $ pip3 install -r requirements.txt
175 | ```
176 |
177 | 9. 创建并启动第一个Odoo实例:
178 |
179 | ```
180 | $ createdb odoo-test
181 | $ python3 odoo-bin -d odoo-test -i base --addons-path=addons --db-filter=odoo-test$
182 | ```
183 |
184 | 10. 在浏览器中访问http://localhost:8069(虚拟机请修改为对应的 IP 地址),并使用admin账户和密码admin来进行登录
185 |
186 | > 📝如需RTL(文字从右向左)的支持,请使用如下命令安装node 和 rtlcss :
187 | >
188 | > ```
189 | > sudo apt-get install nodejs npm -y
190 | > sudo npm install -g rtlcss
191 | > ```
192 |
193 | ### 运行原理...
194 |
195 | 第1步中,我们安装了一些核心依赖。这些依赖包含各类工具,如git, pip3, wget, Python设置工具等。这些核心工作帮助我们使用简单命令安装其它Odoo依赖。
196 |
197 | 第2步中,我们下载并安装了wkhtmltopdf包,用于在Odoo中将销售订单、发票等报告打印为PDF文档。Odoo 14.0要求使用wkhtmltopdf的0.12.5版本,这一版本可能在当前Linux发行版中并未包含。所幸wkhtmltopdf的维护者在http://wkhtmltopdf.org/downloads.html为我们提供了针对各发行版的预构建包,通过该URL可进行下载及安装。
198 |
199 | #### PostgreSQL配置
200 |
201 | 第3步中,我们安装了PostgreSQL数据库。
202 |
203 | 第4步中,我们通过系统登录用户名新建了一个数据库。$(whoami) 用于获取系统用户名,-s选项用于授予超级用户权限。我们来了解下为什么需要这些配置。
204 |
205 | Odoo使用psycopg2 Python库来与PostgreSQL数据库建立连接。通过psycopg2库访问PostgreSQL数据库。Odoo使用如下的默认值:
206 |
207 | - 默认psycopg2尝试使用本地连接的当前用户相同的用户名来连接数据库,这会启动无密码认证(在开发环境中这样很好)
208 | - 本地连接使用Unix域套接字
209 | - 数据库服务监听5432端口
210 |
211 | 这样就好了!PostgreSQL现在就做好了与Odoo建立连接的准备。
212 |
213 | 因为这个是开发服务器,我们对用户授予了--superuser的权限。对生产实例,可以在命令行中使用--createdb来代替--createdb以进行权限限制。在生产服务上--superuser权限会给予黑客在一些部署代码中找到更多漏洞的机会。
214 |
215 | 如果想使用不同的数据库用户,则需提供该用户的密码。通过在创建用户时在命令行传递--pwprompt标记来实现,此时命令行会提示你输入密码。
216 |
217 | 如果用户已存在而你又想要为其设置密码(或修改已忘记的密码),可以使用如下命令:
218 |
219 | ```
220 | $ psql -c "alter role $(whoami) with password 'newpassword'"
221 | ```
222 |
223 | 如果这个命令执行报错提示数据库不存在,那是因为你没按照操作步骤的第4步创建一个与当前登录名相同的数据库。不必担心,仅需使用 --dbname选项来添加已有数据库名,例如 --dbname template1。
224 |
225 | #### Git配置
226 |
227 | 开发环境我们使用GitHub上的Odoo源代码。借助于git可以轻松地在各个Odoo版本间进行切换。同时可以使用git pull命令拉取最新的修改。
228 |
229 | 在第5步中,我们配置了git用户信息。
230 |
231 | 第6步中,我们从Odoo官方GitHub仓库下载了源代码。使用的是git clone命令来下载Odoo源代码。指定了单分支这样只需要下载14.0版本的分支即可。同时还使用了--depth 1来避免下载该分支的完整历史提交。这些选项会让源代码下载变得快速,你也可以在需要的时候省略掉这些选项。
232 |
233 | Odoo开发者还推荐nightly构建,以tar文件和发行包的形式出现。使用git clone最主要的优势是在源代码树中提交了新的bug修复时可以对仓库直接进行更新。还能够轻松地测试所推荐的修复并追踪回退,这样会让bug报告更为精确、对开发者也更有帮助。
234 |
235 | > 📝如果你可以访问企业版源代码,也可以将其下载到~/odoo-dev目录下面单独的文件夹中。
236 |
237 | #### 虚拟环境
238 |
239 | Python虚拟环境或简称virtualenv,是隔离的Python工作空间。这些对于Python开发者非常有用,因为它们允许在Python解释器版本上安装不同版本的Python库。
240 |
241 | 你可以使用python3 -m venv ~/newvenv命令来创建所需数量的环境。这会在指定位置创建一个newenv目录,其中包含一个bin/子目录和一个lib/python3.6(**译者注:**或你安装的其它 Python 3版本)子目录。
242 |
243 | 在第7步中,我们在~/venv-odoo-14.0目录下新建了一个虚拟环境。这就是我们为Odoo准备的独立Python环境。Odoo的所有Python依赖都在这一环境中进行安装。
244 |
245 | 要启用虚拟环境,我们需要使用source命令。通过source ~/venv-odoo-14.0/bin/activate,我们激活了该虚拟环境。
246 |
247 | #### 安装 Python包
248 |
249 | Odoo的源代码中有一系列Python依赖,位于requirements.txt中。第8步中,我们通过pip3 install命令安装了所有这些依赖。
250 |
251 | 这样就可以了,此时即可运行Odoo实例。
252 |
253 | #### 启动实例
254 |
255 | 到了你期待已久的时刻了。要启动我们的第一个实例,第9步中首先我们新建了一个空数据库,然后使用odoo-bin以及如下命令行参数启动Odoo实例:
256 |
257 | ```
258 | python3 odoo-bin -d odoo-test -i base --addons-path=addons --db-filter=odoo-test$
259 | ```
260 |
261 | 可以通过在odoo-bin的前面添加./来省略掉python3,因其是一个可执行的Python脚本,如下:
262 |
263 | ```
264 | ./odoo-bin -d odoo-test –i base --addons-path=addons --db-filter=odoo-test$
265 | ```
266 |
267 | odoo-bin后使用了如下命令行参数:
268 |
269 | - -d database_name:默认使用这一数据库。
270 | - --db-filter=database_name$:仅尝试连接匹配所提供正则表达式的数据库。一个Odoo安装可以为使用不同数据库的多个实例提供服务,通过这一参数限制可用的数据库。最后的那个$很重要,因为在匹配模式中使用了正则表达式,这会避免选择以相同的指定字符串开头的名称。
271 | - --addons-path=directory1,directory2,...:Odoo通过这一逗号分隔列表中的目录来查找插件(add-on)。在实例创建的时候扫描该列表来添加实例中可用的插件模块列表。如果希望使用Odoo企业版,请在这一选项中添加其目录。
272 |
273 | - -i base: 用于安装base模块。通过命令行创建数据库时需要使用到。
274 |
275 | 如果你使用了与Linux当前登录用户不同的数据库用户,则需要再传递如下的参数:
276 |
277 | - --db_host=localhost: 使用TCP连接数据库服务
278 | - --db_user=database_username: 使用指定的数据库登录用户
279 | - --db_password=database_password: 这是用于认证PostgreSQL服务的密码
280 |
281 | 使用--help可获取所有可用选项的一个总览。我们在本章后面还会来了解odoo-bin脚本更多的知识。
282 |
283 | Odoo在一个空数据库上启动时,它会首先创建一个支持其操作所需的数据库结构。还会扫描插件路径来查找可用的插件模块,并将一些内容插入到数据库的初始记录中。这包括admin用户及默认admin密码,在登录时将使用到。
284 |
285 | 在浏览器中访问http://localhost:8069/( **译者注:**非本地请自行修改为 IP 地址)会访问到你所新创建的实例的登录页面,如下图所示:
286 |
287 | 
288 |
289 | 图1.2 - Odoo实例的登录页面
290 |
291 | 这是因为Odoo内置有HTTP服务器。默认,它监听TCP端口8069上的所有本地网络接口。
292 |
293 | ## 管理Odoo服务端数据库
294 |
295 | 在使用Odoo时,你的实例所有的数据都存储在PostgreSQL数据库中。可以使用你习惯的任意标准数据库管理工具,但Odoo为一些常用操作提供了一个网页界面。
296 |
297 | ### 准备工作
298 |
299 | 我们假定你的工作环境已配置好,并且已运行了一个实例。
300 |
301 | ### 如何管理...
302 |
303 | Odoo数据库管理界面提供创建、复制、删除、备份和恢复数据库的工具。还有一种修改主控密码(master密码)的方式,用于保护对数据库管理界面的访问。
304 |
305 | #### 访问数据库管理界面
306 |
307 | 需要执行如下步骤来访问数据库:
308 |
309 | 1. 进入实例的登录页面(如果已登录请先登出)。
310 | 2. 点击Manage Databases链接。这会导航至http://localhost:8069/web/database/manager(也可以在浏览器中直接访问这个URL)。
311 |
312 | 
313 |
314 | 图1.3 – 数据库管理器
315 |
316 | #### 设置或修改主控密码
317 |
318 | 如果已经以默认值设置了实例且尚未像下面讲解的那样做过修改,数据库管理页面会显示一条警告,告诉你还没有设置主控密码并建议你通过直接点击链接来进行设置:
319 |
320 | 
321 |
322 | 图1.4 – 主控密码警告
323 |
324 | 需要执行如下步骤来设置master:
325 |
326 | 1. 点击Set Master Password按钮。会弹出一个对话框来要求你提供新的主控密码:
327 | [](https://alanhou.org/homepage/wp-content/uploads/2019/05/2019050505371024.jpg)图1.5 – 设置新主控密码对话框
328 | 2. 输入一个复杂的新密码并点击Continue
329 |
330 | 如果已设置了主控密码,点击页面底部的Set Master Password按钮来进行修改。在弹出的对话框中输入老的主控密码再输入新密码,然后点击Continue。
331 |
332 | > 📝主控密码是在admin_password键下的服务端配置。如果启动服务时没有指定配置文件,会在~/.odoorc中生成新的配置文件。查看下一部分获取更多有关配置文件的信息。
333 |
334 | #### 创建新数据库
335 |
336 | 这个对话框用于创建一个由当前Odoo服务处理的新数据库实例:
337 |
338 | 1. 在数据库管理窗口中,点击页面底部的Create Database按钮。会弹出如下对话框:
339 | [](https://alanhou.org/homepage/wp-content/uploads/2019/05/2019050505390961.jpg)图1.6 – 新建数据库对话框
340 |
341 | 2. 填写表单,如下:
342 |
343 | - Master Password:这是这一实例的主控密码。
344 | - Database Name:输入所想要创建的数据库名称。
345 | - Email: 在此处添加email地址;在稍后用作用户名。
346 | - Password:输入你想为新实例所设置的admin用户密码。
347 | - Phone Number:设置电话号码(可选)。
348 | - Language:在下拉列表中选择你希望新数据库默认安装的语言。Odoo会自动加载所选语言的翻译。
349 | - Country:在下拉列表中选择主租户的国家。选择这一项后会自动做一些配置,如公司的币种。
350 | - Demo data:勾选获取演示数据。这对于运行交互式测试或为用户设置演示项目非常有用,但针对包含生产数据的数据库设计时则不应勾选。
351 |
352 | > ℹ️如果想要使用该数据库来运行模块的自动化测试(参见[第七章 调试](7.md))),则需要有演示数据,因为Odoo中的大多数自动化测试依赖于这些记录来成功运行。
353 |
354 | 3. 点击Continue按钮并等待新数据库初始化完成。然后会被重定向到该实例并以管理员进行连接。
355 |
356 | > 问题处理:如果被重定向到了登录页,这可能是因为向Odoo传递了--db-filter选项并且与新数据库名不匹配造成的。注意odoo-bin start会默默地进行这一操作,仅让当前数据库可用。解决这一问题,只需不使用start命令来对Odoo进行重启,在本章中的*通过源码轻松安装 Odoo* 一节展示过这一做法。如果你有一个配置文件(参见本章后面的*在一个文件中存储实例配置*一节),那么请检查db_filter未进行设置或设置一个与新数据库名称匹配的值。
357 |
358 | #### 复制数据库
359 |
360 | 通常你已经有一个数据库了,会希望使用它做一存储过程的试验或运行测试,但又不修改已有数据。这里的方案很简单:复制该数据库并在拷贝上运行测试。可以按需重复多次:
361 |
362 | 1. 在数据库管理页面,点击Duplicate Database:
363 | [](https://alanhou.org/homepage/wp-content/uploads/2019/05/2019050515123745.jpg)图1.7 – 复制数据库对话框
364 | 2. 填写表单如下:
365 | - Master Password:这是Odoo服务的主控密码
366 | - New Name:给予拷贝的名称
367 | 3. 点击Continue按钮。
368 | 4. 可以在数据库管理页面点击新创建的数据库名称来访问该数据库的登录页面。
369 |
370 | #### 删除数据库
371 |
372 | 在完成测试之后,如想要清理掉所复制的数据库。通过如下步骤来进行清理:
373 |
374 | 1. 在数据库管理页面,点击想删除的数据库名称旁的Delete Database链接,会出现下图中的对话框:
375 | [](https://alanhou.org/homepage/wp-content/uploads/2019/05/2019050515230631.jpg)图1.8 – 删除数据库弹窗
376 | 2. 填写表单并输入 Master Password,即Odoo服务的主控密码。
377 | 3. 点击Delete按钮。
378 |
379 | > 📝小心!数据丢失!
380 | >
381 | > 如果你选择了错误的数据库,并且没有备份,则无法恢复损失的数据。
382 |
383 | #### 备份数据库
384 |
385 | 需执行如下步骤来创建备份:
386 |
387 | 1. 在数据库管理页面,点击想备份数据库名称旁的Backup按钮,会弹出下图这样的对话框:
388 | [](https://alanhou.org/homepage/wp-content/uploads/2019/05/2019050516024419.jpg)图1.9 – 备份数据库弹窗
389 | 2. 填写表单
390 | - Master Password:Odoo服务的主控密码。
391 | - Backup Format:对生产数据库请保持使用zip,因为这是唯一真正的全量备份格式。仅在备份不关心文件存储的开发数据库时使用pg_dump格式。
392 | 3. 点击Backup。然后浏览器会下载备份文件。
393 |
394 | #### 还原数据库备份
395 |
396 | 如果需要还原一个备份,需要按如下步骤操作:
397 |
398 | 1. 在数据库管理页面,点击页面底部的Restore Database按钮,会弹出如下的对话框:
399 | [](https://alanhou.org/homepage/wp-content/uploads/2019/05/2019050516123082.jpg)图1.10 – 还原数据库对话框
400 | 2. 填写表单:
401 | - Master Password:这是Odoo服务的主控密码。
402 | - File:这是之前所下载的Odoo备份
403 | - Database Name:提供你需进行备份还原的数据库名称。该数据库在服务器上一定不能存在。
404 | - 数据库可能进行了迁移或拷贝:如果原数据库在另一台服务器上或是为从当前服务器删除则选择This database was moved。否则选择This database is a copy,这也是安全的默认选项。
405 | 3. 点击Continue按钮。
406 |
407 | > 📝不能在数据库自身之上还原数据库。这么做会得到一条错误消息(Database restore error: Database already exists)。需要先删除该数据库。
408 |
409 | ### 运行原理...
410 |
411 | 除Change master password以外的这些页面上的功能,是在服务器上运行PostgreSQL运维命令并通过网页界面报告结果。
412 |
413 | 主控密码是非常重要的信息,仅存储在Odoo服务的配置文件中,从不在数据库中进行存储。曾经有一个admin默认值,但使用这个值是一个广为人知的安全问题。在Odoo v9及之后的版本中,这被识别为一个未设置的主控密码,并在访问数据库管理页面时会敦促你修改密码。虽然这在配置文件中以admin_passwd进行存储,它与admin的密码是不同的,它们是两个不同的密码。主控密码是为Odoo服务进程设置的,进程本身可以处理多个数据库实例,每个实例都有一个独立的admin用户及其自己的密码。
414 |
415 | > 📝安全考虑:记住本章中我们所考虑的是开发环境。Odoo数据库管理界面在我们运行生产服务时是需要进行安全保护的,因为这里给到了过多敏感信息的访问权限,尤其是在服务器托管了多个不同客户端的Odoo实例时。
416 |
417 | Odoo使用PostgreSQL的createdb工具来新建数据库,它通过和以空数据库启动Odoo时相同的方式调用内部的Odoo函数来初始化新数据库。
418 |
419 | Odoo使用createdb的--template选项传递原数据库作为参数来复制数据库。这基本上使用内部优化的PostgreSQL例行程序在新数据库中复制模板数据库的结构,这比创建备份和还原备份的速度会快很多(尤其是在使用网页界面时,还要求你下载备份文件然后再重新上传)。
420 |
421 | 备份和还原操作分别使用pg_dump和pg_restore工具。在使用zip格式时,备份还包含文件存储的拷贝,其中为配置Odoo不保存在数据库中的文档的拷贝,这是14.0中的默认选项。如果没做过修改的话,这些文件存放在~/.local/share/Odoo/filestore中。
422 |
423 | > 📝如果备份很大,下载时会失败。这可能是因为Odoo服务本身无法在内存中处理这么大的文件或者是因为服务在反向代理之后运行,而这个代理设置了HTTP响应大小的限制。反过来,出于某些原因,你可能会在还原数据库的操作中遇到问题。在碰到这些问题时,应当投入时间建立更健壮的外部备份方案。
424 |
425 | ### 更多内容...
426 |
427 | 有经验的Odoo开发者通常不使用数据库管理界面,而在命令行执行相关操作。比如使用演示数据初始化新数据库,可以使用如下的一行代码:
428 |
429 | ```
430 | $ createdb testdb && odoo-bin -d testdb
431 | ```
432 |
433 | 命令行的另一个彩蛋是可以在使用时要求安装一些插件,比如 -i sale,purchase,stock。
434 |
435 | 停止服务并运行如下这些命令来复制数据库:
436 |
437 | ```
438 | $ createdb -T dbname newdbname
439 | $ cd ~/.local/share/Odoo/filestore # 如果你修改了data_dir请调整此处
440 | $ cp -r dbname newdbname
441 | $ cd -
442 | ```
443 |
444 | 注意在开发的上下文中,文件存储通常会被省略。
445 |
446 | > 📝createdb -T 仅在数据库没有活跃会话时方能使用,这表示在通过命令行复制数据库之前你需要关闭Odoo服务。
447 |
448 | 可运行如下命令来删除一个实例:
449 |
450 | ```
451 | $ dropdb dbname
452 | $ rm -rf ~/.local/share/Odoo/filestore/dbname
453 | ```
454 |
455 | 可运行如下命令来创建一个备份(假设PostgreSQL服务在本地运行):
456 |
457 | ```
458 | $ pg_dump -Fc -f dbname.dump dbname
459 | $ tar cjf dbname.tgz dbname.dump ~/.local/share/Odoo/filestore/dbname
460 | ```
461 |
462 | 可运行如下命令来还原备份:
463 |
464 | ```
465 | $ tar xf dbname.tgz
466 | $ pg_restore -C -d dbname dbname.dump
467 | ```
468 |
469 | > 📝**当心!**
470 | >
471 | > 如果你的Odoo实例使用了另一个用户连接数据库,需要传递-U username来使用正确的用户作为还原数据库的所有者。
472 |
473 | ## 在文件中存储实例配置
474 |
475 | odoo-bin脚本有几十个选项,记住所有这些以及记得在启动服务时适当地进行配置会非常单调费力。所幸可以将它们存储在一个配置文件中,只需对想要修改的选项进行手动修改,比如为开发环境做修改。
476 |
477 | ### 如何配置
478 |
479 | 对于本小节可执行如下步骤:
480 |
481 | 1. 运行如下命令来为你的Odoo实例生成一个配置文件:
482 |
483 | ```
484 | $ ./odoo-bin --save --config myodoo.cfg --stop-after-init
485 | ```
486 |
487 | 2. 还可以添加其它选项,它们的值会被保存到所生成的文件中。所有未设置的值都会以默认值进行保存。使用如下命令来获取可用的选项列表:
488 |
489 | ```
490 | $ ./odoo-bin --help | less
491 | ```
492 |
493 | 这会提供一些不同选项所执行内容的帮助文档。
494 |
495 | 3. 要从命令行形式转化为配置形式,使用长选项名,删除前面的中间杠,并将中间的中间杠转换为下划线。--without-demo就变成了without_demo。对大多数选项都是如此,但有一些例外,在下一部分中会列出。
496 |
497 | 4. 编辑myodoo.cfg文件(使用下一部分中的表格来查看所要修改的参数)。然后运行如下命令来以所保存的选项启动服务:
498 |
499 | ```
500 | $ ./odoo-bin -c myodoo.cfg
501 | ```
502 |
503 | > 📝-config选项通常简写为-c。
504 |
505 | ### 运行原理
506 |
507 | 启动时,Odoo通过三个步骤来加载它的配置。首先,所有选项的一组默认值会从源码中进行初始化,然后解析配置文件,该文件中所定义的任意值会覆盖默认值。最后,会分析命令行选项,它们的值会覆盖前面步骤中所获取的配置。
508 |
509 | 前面我们已提到,配置变量的名称可通过删除命令行选项的前置中间杠以及将中间的连接符转换为下划线来获取。其中有一些例外,特别是下面这些:
510 |
511 | | 命令行 | 配置文件 |
512 | | :-------------------------- | :----------------------- |
513 | | --db-filter | dbfilter |
514 | | --no-http | http_enable = True/False |
515 | | --database | db_name |
516 | | --dev | dev_mode |
517 | | --i18n-import/--i18n-export | 不可用 |
518 |
519 | 表1.2
520 |
521 | 以下是通过配置文件设置的常用选项列表:
522 |
523 | | 选项 | 格式 | 用途 |
524 | | :------------------------- | :------------------- | :----------------------------------------------------------- |
525 | | without_demo | 逗号分隔的模块名列表 | 该选项阻止模块演示数据被加载。 设置为all取消所有模块的演示数据,设为False为所有模块启用演示数据。对具体模块禁用演示数据,应提供模块名,如 sale,purchase,crm。 |
526 | | addons_path | 逗号分隔的路径列表 | 这是一个服务查找插件的路径名列表。 |
527 | | admin_passwd | 文本 | 这是 master 密码(参见前面部分的内容) |
528 | | data_dir | 一个目录路径 | 这个目录中服务会存储session信息、从网上下载的插件以及在启用了文件存储时存放文档。 |
529 | | http_interface | 网络接口的 IP 地址 | 默认为0.0.0.0,表示服务监听所有接口。 |
530 | | http_port longpolling_port | 端口号 | 这些是 Odoo 服务所会监听的端口。你需要指定这两者来在同一台主机上运行多个 Odoo 服务;longpolling_port仅在workers不为0时使用。 http_port默认值为8069,longpolling_port默认为8072。 |
531 | | logfile | 文件路径 | Odoo 写入日志的文件。 |
532 | | log_level | 日志信息级别 | 指定日志的级别。可接受的值(内容逐渐增加)包括critical, error, warn, info, debug, debug_rpc, debug_rpc_answer, debug_sql。 |
533 | | workers | 整数 | worker进程的数量,更多信息参见[第三章 服务器部署](3.md)。 |
534 | | proxy_mode | True/False | 激活反向代理WSGI封装。仅在运行于可信任的 web 代理后启用它。 |
535 |
536 | 表1.3
537 |
538 | 以下是与数据库相关的配置选项列表:
539 |
540 | | 选项 | 格式 | 用途 |
541 | | :---------- | :------------- | :----------------------------------------------------------- |
542 | | db_host | 主机名 | 这是运行PostgreSQL服务的服务器名。使用 False 来使用本地 Unix 域套接字,以及 localhost 来使用本地 TCP 套接字。 |
543 | | db_user | 数据库登录用户 | 在db_host为 False 时这通常为空。这将是用于连接数据库的用户。 |
544 | | db_password | 数据库用户密码 | 在db_host为 False以及 db_user 与运行服务的用户相同时通常为空。阅读pg_hba.conf的主页面来获取更多相关信息。 |
545 | | db_name | 数据库名 | 用于设置一些默认执行命令操作的数据库名。这不会限制服务所操作的数据库。参照下面的 dbfilter 参数。 |
546 | | db_sslmode | 数据库SSL模式 | 用于指定数据库SSL连接模式。 |
547 | | dbfilter | 一个正则表达式 | 该表达式应匹配服务所使用的数据库名。如果你运行网站,应该匹配单个数据库,类似^databasename$。更多相关信息请参见[第三章 服务器部署](3.md)。 |
548 | | list_db | True/False | 设置为 True 来取消列出数据库。更多信息请参见[第三章 服务器部署](3.md)。 |
549 |
550 | 表1.4
551 |
552 | **译者注:**表中的pg_hba.conf文件位置:/etc/postgresql/xxx/main/pg_hba.conf,另表中的[第三章 服务器部署](3.md)为上一版中的内容,在本书中已不再包含。
553 |
554 | Odoo对配置文件的解析现在使用Python的ConfigParser模块。但是在Odoo 11.0中的实现发生了变化,它不再支持使用变量插值。因此,如果你习惯了使用%(section.variable)s表达式通过其它变量的值定义变量值的话,需要改变这一习惯并恢复使用显式的值。
555 |
556 | 有些选项不在配置文件使用,但广泛用于开发之中:
557 |
558 | | 选项 | 格式 | 用途 |
559 | | :----------- | :------------------------------- | :----------------------------------------------------------- |
560 | | -i或--init | 逗号分隔的模块名列表 | 它会在初始化数据库时默认安装给定的模块 |
561 | | -u 或-update | 逗号分隔的模块名列表 | 它会在重启服务时更新给定的模块。多在修改了源代码或从 git 更新了分支时使用 |
562 | | --dev | all, reload, qweb, werkzeug, xml | 这会启用开发者模式及自动重新加载功能。 |
563 |
564 | 表1.5
565 |
566 | ## 激活Odoo开发者工具
567 |
568 | 开发人员在使用Odoo时,应当知道如何在网页界面激活开发者模式,这样你就可以访问技术设置菜单及开发者信息。启动调试模式会暴露出一些高级配置项及字段。Odoo隐藏这些选项和字段来实现更好的易用性,因为日常不会使用到它们。
569 |
570 | ### 如何激活
571 |
572 | 按照如下步骤来在网页界面中激活开发者模式:
573 |
574 | 1. 连接到你的实例并以 admin 登录
575 | 2. 访问Settings菜单
576 | 3. 滚动至页面底部,找到Developer Tools版块
577 | 
578 | 图1.11 – 启用不同开发者模式的链接
579 | 4. 点击Activate the developer mode链接
580 | 5. 等待用户界面重载
581 |
582 | > ℹ️**其它方式:**也可以通过编辑 URL 来激活开发者模式。在链接的#号前,插入?debug=1。例如,如果你的链接是http://localhost:8069/web#menu_id=102&action=94,那么你需要将其修改为http://localhost:8069/web?debug=1#menu_id=102&action=94。此外,如果你想要使用带静态文件的调试模式,则将 URL修改为http://localhost:8069/web?debug=assets#menu_id=102&action=94
583 |
584 | **译者注:**加(with assets)的模式会将静态文件(css, js)分拆每一个具体文件,这将有助于调试,但相对于合并的静态文件而言会损失一些加载速度
585 |
586 | 通过如下其中一种方式可退出开发者模式:
587 |
588 | - 编辑URL并在查询字符串中写入?debug=0
589 | - 通过使用Settings菜单相同位置下的Deactivate the developer mode链接
590 | - 点击顶部调试小虫图标,在下拉菜单中点击Leave Developer Tools选项
591 |
592 | 很多开发者使用浏览器插件来切换调试者模式。通过使用插件,可以无需访问settings菜单快速地切换调试模式。这些插件可在Firefox和Chrome浏览器中使用。参见如下截图,它有助于你在Chrome商店中找到该插件:
593 |
594 | [](https://alanhou.org/homepage/wp-content/uploads/2019/05/2019050613255271.jpg)
595 |
596 | 图1.12 – 调试模式的浏览器插件
597 |
598 | > 📝调试模式的行为从Odoo v13开始发生了改变。从v13开始,调试模式的状态存储在会话中,那么即使在URL中删除掉?debug,调试模式依然处于启动状态。
599 |
600 | ### 运行原理
601 |
602 | 开发者模式中,会发生两件事情:
603 |
604 | - 鼠标在表单视图的字段上或列表视图的列名上悬浮时会给出提示信息,提供该字段的技术信息(内部名称、类型等)。
605 | - 调试图标下拉菜单会显示在右上角用户菜单旁,给到显示的模型相关技术信息的访问,有各种关联的视图定义、工作流、自定义过滤管理等等。
606 |
607 | 开发模式有一个变体:Developer mode (with assets)。这一模式和普通的开发者模式相似,但除此之外,发送到浏览器的JavaScript 和 CSS没有做最小化处理,这表示你浏览器的web开发者工具可以方便地用于调试JavaScript代码(更多内容请见[第十五章 网页客户端开发](15.md))。
608 |
609 | > 📝**注意!**
610 | >
611 | > 使用非开发者模式及开发者模式来测试你的插件,因JavaScript库的非最小化版本会隐藏最小化版本中伤你至深的 bug。
612 |
613 | ## 更新插件模块列表
614 |
615 | 在新增模块时,Odoo并不知道新模块的存在。为在Odoo中列出该模块,需要更新模块列表。本小节中学习如何更新应用列表。
616 |
617 | ### 准备工作
618 |
619 | 启动实例并使用Administrator账号连接实例。然后启用开发者模式(如尚不知道如何启用开发者模式,参见[第一章 安装Odoo开发环境](1.md))。
620 |
621 | ### 如何实现...
622 |
623 | 在实例中更新已有的插件模块列表,执行如下步骤:
624 |
625 | 1. 打开Apps菜单**。**
626 | 2. 点击Update Apps List。
627 | 
628 | 图1.13 – 更新应用列表的菜单项
629 | 3. 在弹出的对话框中点击Update按钮:
630 | 
631 | 图1.14 – 更新应用列表对话框
632 | 4. 在更新结束时,可以点击Apps查看更新后的可用插件模块列表。需要删除掉Apps搜索框中默认的过滤器来查看所有模块。
633 |
634 | ### 运行原理...
635 |
636 | 在点击Update按钮时,Odoo会读取插件路径配置变量。对于列表中的每个路径,它会查找插件声明文件包含的直接子目录,声明文件__manifest__.py存储在插件模块目录下。Odoo读取声明内容,查找其中的Python字典。只要声明中键为installable的实例不设置为False,就会将插件模块元数据存储到数据库中。如果模块已存在,会更新信息。如不存在,会新建一条记录。如果此前可用的插件模块查找不到,也不会从列表中进行删除。
637 |
638 | > 📝仅在初始化数据库后新增插件路径时才需要更新应用列表。如果在初始化数据库之前在配置文件中新增了插件路径,则无需手动更新模块列表。
639 |
640 | 总结一下我们目前所学到的知识,在完成安装后,我们使用如下命令启动Odoo服务(如果使用了虚拟环境,需要先激活该环境):
641 |
642 | ```
643 | python3 odoo-bin -d odoo-test -i base --addons-path=addons --db-filter=odoo-test
644 | ```
645 |
646 | 运行后,可以通过http://localhost:8069来访问Odoo。
647 |
648 | 还可以使用配置文件来运行Odoo,如下:
649 |
650 | ```
651 | ./odoo-bin -c myodoo.cfg
652 | ```
653 |
654 | 启动好Odoo服务后,可以在Apps 菜单下安装/更新其中的模块。
655 |
656 |
--------------------------------------------------------------------------------