可以参考著名的http://todomvc.com/ 项目。
我们将创建一个简单的 ToDo 应用,它将允许我们添加新的任务,当任务完成的时候标记它们,并最终清除所有的已完成任务的列表。
Odoo遵循 MVC-like 架构设计,当实现 To-Do 应用的时候,我们会接触不同的层。
模型层,定义app 的数据结构
视图层,描述用户界面
控制器层,支持应用的业务逻辑
2.1.基本概念
2.1.1.了解应用程序和模块
在Odoo中,经常听见模块(modules)
和应用程序(application)
。那么,它们有什么区别呢?
模块元件是Odoo应用程序的组成块。一个模块可以将新功能添加到 Odoo,或修改现有的功能。 它是一个包含名为manifest.py的清单或描述符文件的目录,再加上实现其功能的其余文件。
应用程序是将主要功能添加到Odoo的方式。他们为核心功能元素,如会计或人力资源,其它的模块在此基础上修改或扩展功能。正因为如此,他们在Odoo应用程序菜单中高亮显示。
如果您的模块很复杂,并将新的或重大的功能添加到 Odoo,您可以考虑将它创建为应用程序。如果您的模块只是对现有的 Odoo 中的功能进行更改,则可能不需要将它创建为应用程序。
在清单中定义模块是否是应用程序。技术上它在模块使用上,没有任何的影响。它只是在应用程序清单中高亮显示。
2.1.2.修改和扩展模块
在示例中,我们要记住,在新模块创建时,要尽可能少的使用依赖关系。然而,这不是常见情况。大多数情况下,我们将修改或扩展现有的模块。
一般情况下,通过直接更改它们的源代码修改现有模块的功能是一个很糟糕的方法。对于 Odoo 提供的官方模块,尤其如此。如果这样做,你不能明确的区分原始模块代码和修改,并且这使它难以升级,因为升级会覆盖所做的修改。相反,我们应该创建一个额外的模块,去实现我们需要改变或者增加的功能。事实上,Odoo 的主要优势之一是继承机制,允许自定义模块扩展现有模块,无论是官方的还是社区的。
继承可以是任意一个层次︰ 数据模型、业务逻辑和用户界面层。一旦我们可以轻松的创建一个新的模块,我们可以深入的学习继承机制。
2.1.3.创建模块的基本框架
Odoo包括一个scaffold命令来自动创建一个新的模块目录,其基本结构已经就位。 你可以用下面的命令来了解更多。
$ ~/odoo-dev/odoo/odoo-bin scaffold --help
在处理下一个模块时,您可能想要记住这一点,但是我们现在不会使用它,因为我们宁愿手动创建模块的所有结构。
Odoo addon模块是一个包含 manifest.py 描述文件的目录
备注
在以前的版本中,此描述符文件命名为 openerp.py。 此名称仍然受支持,但已被弃用
它还需要是 Python 可导入的,因此它还必须有一个 init.py 文件。模块的目录名称是其技术名称。 我们将使用todo_app。 文件名称必须是有效的Python标识符:它应以字母开头,并且只能包含字母,数字和下划线字符。
以下命令将创建模块目录并在其中创建一个空的init.py文件,〜/ odoo-dev / customaddons / todo_app / __ init__.py 如果想通过命令行,下面的命令行你将会用到:
$ mkdir ~/odoo-dev/custom-addons/todo_app
$ touch ~/odoo-dev/custom-addons/todo_app/__init__.py
接下来,我们需要创建清单文件。 它只包含一个Python字典,里面大约有十几个可能的属性; 其中,只有 name 属性是必填的。描述属性(对于较长的描述)和作者属性是建议填写的。 我们现在应该在init.py文件旁边添加一个manifest.py文件,其中包含以下内容
{
'name': 'To-Do Application',
'description': 'Manage your personal To-Do tasks.',
'author': 'Daniel Reis',
'depends': ['base'],
'application': True,
}
depends 属性可以包含所依赖的其他模块的列表。 当安装此模块时,Odoo 将自动安装它们。 这不是一个强制性的属性,但建议总是包含它。 如果不需要特定的依赖,我们应该依赖于核心 base 模块。你应该注意确保所有依赖关系在这里明确设置; 如果其所依赖的模块在其之后加载,模块可能无法安装在干净的数据库(由于缺少依赖关系)或有加载错误。
对于我们的应用程序,我们不需要任何特定的依赖关系,因此我们仅依赖于 base 模块 为了简明起见,我们选择使用非常少的描述键,但是在真实的情况下,我们建议您还使用附加键,因为它们与 Odoo 应用商店相关:
summary 显示为模块的副标题
version 默认为 1.0。 它应该遵循版本语义规则(详见http://semver.org/)
license 许可证标识符,默认为LGPL-3
website 网站是一个用于查找有关模块的更多信息的URL。 这可以帮助人们找到更多的文档或问题跟踪,以提出 Bug 和建议。
category是模块的功能类别,默认为Uncategorized。 现有类别的列表可以在【应用程序】下拉列表中的安全组表单(设置|用户|组)中找到其它可用的描述符:
installable 默认为 True,但可以设置为 False 以禁用模块
auto_install 如果设置为True,此模块将被自动安装,前提是其所有依赖项都已安装。 它用于必装模块
从Odoo 8.0 开始,我们可以在模块的顶层目录中使用 README.rst 或 README.md 文件替代描述键。
2.1.4.关于许可证
为您的工作选择许可证非常重要,您应该仔细考虑您最佳的选择及其影响。 Odoo模块使用最多的许可证是GNU通用公共许可证(LGLP)和Affero通用公共许可证(AGPL)。 LGPL更为宽容,允许商业衍生工作,而不需要共享相应的源代码。 AGPL是一个更强大的开源许可证,需要派生工作和服务托管来共享其源代码。 请访问https://www.gnu.org/licenses/了解有关GNU许可证的更多信息。
2.1.5.添加 addons 路径
现在我们有一个简单的新模块,我们希望将它提供给 Odoo 实例。 为此,我们需要确保包含该模块的目录位于addons路径中,然后更新Odoo模块列表。 在上一章中已经详细解释了这两个动作,但是在这里,我们将继续简要介绍一下需要的内容。 我们将在我们的工作目录中定位并使用适当的addons路径配置启动服务器:
$ cd ~/odoo-dev
$ ./odoo/odoo-bin -d todo --addons-path ="custom-addons, odoo/addons" --save
--save选项保存您在配置文件中使用的选项。这使我们在重新启动服务器时重复使用它们:只需运行./odoobin,并使用上次保存的选项。 仔细查看服务器日志。应该有一个INFO? odoo:addons paths:[...] line。它应该包括我们的 custom-addons 目录。 记住还要包括您可能正在使用的任何其他插件目录。例如,如果您还有一个 ~/odoo-dev/extra 目录,其中包含要使用的其他模块,则可能还要使用--addons-path选项来包含它们:
--addons-path =“custom-addons, extra, odoo/addons”
现在我们需要Odoo实例来识别刚添加的新模块。
2.1.6.安装新模块
在“应用程序”顶部菜单中,选择“更新应用程序列表”选项。这将更新模块列表,添加自上次更新以来可能添加到列表中的任何模块。请记住,我们需要启用开发人员模式才
能看到。
注意
确保您的Web客户端会话正在使用正确的数据库。您可以在右上角查看:数据库名称显示在括号中,紧随用户名。强制使用正确数据库的方法是使用附加选项
--db-filter = ^MYDB$
启动服务器实例。
应用程序选项向我们显示可用模块的列表。默认情况下,它只显示应用程序模块。由于我们创建了一个应用程序模块,因此我们不需要删除该过滤器来查看它。在搜索中键入todo,您应该看到我们的新模块。
2.1.7.升级模块
开发一个模块是一个迭代的过程,它会把你在源代码中更改的功能,在 Odoo 中体现出来。在大多数情况下,只需要升级你的模块:在应用程序列表中的模块,一旦它已经安装,你就可以使用升级按钮。然而,如果只修改了 Python 代码,升级就可能没有效果。除了模块的升级,还需要重新启动应用程序服务器。只要 Odoo 成功加载了 Python 代码,以后任何代码的更改都需要重新启动服务器才能应用。
在某些情况下,如果模块更改了数据文件和 Python 代码,您可能需要同时做这两种操作。对于Odoo新开发人员来说,这是很容易混淆。但幸运的是,还有更好的方式。更改我们的模块最安全和最快的方式,是停止并重新启动服务器,并按要求把我们的模块升级到我们工作的数据库。在终端服务器实例运行时,使用ctrl 键 + C 来停止它。然后,启动服务器和升级 todo_app 模块,使用下面的命令︰
python odoo-bin -c /etc/odoo10-server.conf --stop-after-init -d xxxx -u all
$./odoo-bin -d todo -u todo_app
-u 选项(或 —— update) 需要 -d 选项,并需要一个逗号分隔更新的模块列表。例如,我们可以使用 -u todo_app,mail。一个模块更新时,也将更新所有其他依赖它的已安装的模块。 -all 是更新所有模块。
这是必须保持的继承机制,用于扩展功能的完整性。
总体来说,当你需要更新应用模块时,最安全的方法是利用前面讲述的命令重新启动 Odoo 实例。按 up 键就可以使用前面的命令。所以,大多数情况下,你会发现自己经常使用 Ctrl + C ,up 和 Enter 键组合。
值得特别注意的是,更新模块列表和卸载模块这两种操作,都不可以通过命令行操作。需要在 web 界面的应用程序菜单中进行。
2.1.8.服务器开发模式
Odoo 10 新的选项特性,更方便开发者进行开发。要使用它,在启动服务器实例时,附加选项--dev=all。这有几个小技巧,可以缩短我们的开发周期。最重要的是︰
一旦保存Python文件,自动重新加载 Python 代码,避免手动重启服务器;从 XML 文件中,直接获取视图定义,避免手动模块升级;
--dev选项接受一个逗号分隔的列表,然而大部分时间都使用的是合适的all选项。我们还可以指定使用的调试器。默认情况下,Python 调试器,使用pdb 。有些人可能喜欢安装和使用其他调试程序。它也支持ipdb和 pudb调试。
2.2.模型层
让我们开始在Odoo中添加一个简单的新模型。
模型描述业务对象,例如商机、销售订单或合作伙伴(客户,供应商等)。 模型具有属性列表,并且还可以定义特定的业务逻辑。
模型是从 Odoo 模板类派生的 Python 类实现的。 它们直接转换为数据库对象,Odoo在安装或升级模块时会自动处理。负责此机制的是对象关系模型(ORM)。
我们的模块将是一个非常简单,记录保留待办事项的应用程序。
应用程序的主要功能有:用于描述的简单文本字段、用于将其标记为完成的复选框、最后需要添加一个按钮来清除已完成任务的旧的待办事项列表。
2.2.1.创建数据模型
Odoo 开发指导指出,模型的 Python 文件应放在一个 models 子目录中。为了简单起见,我们先不遵循这一准则,我们在 todo_app 模块的主目录中创建一个todo_model.py文件。在文件中添加以下内容:
# -*- coding: utf-8 -*-
from odoo import models, fields
class TodoTask(models.Model):
_name = 'todo.task'
_description = 'To-do Task'
name = fields.Char('Description', required=True)
is_done = fields.Boolean('Done?')
active = fields.Boolean('Active?', default=True)
第一行是一个特殊的标记告诉Python解释器,这个文件有UTF-8,以便它可以期望和处理非ASCII字符。我们不会使用任何非 ANSI 字符,但无论如何这是一个很好的做法。
第二行是Python代码import语句,从Odoo核心导入模型和字段对象。
第三行声明了我们的新模型。它是从models.Model派生的类。
第四行设置_name属性,定义将在整个Odoo中引用此模型的标识符。注意,实际的Python类名,在这个例子中,TodoTask对其他Odoo模块是无意义的。 _name值将用作标识符。
请注意,此行和以下行是缩进的。如果你不熟悉Python,你必须知道这是很重要的:缩进定义一个嵌套的代码块,所以这四行应该是同样缩进。
然后我们有_description模型属性。它不是强制性的,但它为模型记录提供了一个用户友好的名称,可用于用户友好的消息。
最后三行定义模型的字段。值得注意的是name和active是特殊的字段名。默认情况下,当从其他模型引用它时,Odoo将使用name字段作为记录的标题。active字段用于停用记录,默认情况下,仅显示活动记录。我们将使用它来清除已完成的任务,而不会从数据库中删除它们。
现在,该文件尚未被模块使用。我们必须告诉Python使用init.py文件中的模块加载它。让我们编辑它以添加以下行:
from . import todo_model
就是这样而已!为了使我们的Python代码更改生效,服务器实例需要重新启动(除非它使用--dev模式)。
我们不会看到任何菜单选项来访问这个新模型,因为我们还没有添加它们。我们仍然可以使用技术菜单查看新创建的模型。在顶部菜单设置中,转到技术|数据库结构|模型,在列表中搜索todo.task模型,并单击它以查看其定义:
如果一切正常,则确认已创建模型和字段。如果您在此处看不到它们,请尝试重新启动服务器并进行模块升级,如前所述。
我们还可以看到一些我们没有声明的额外字段。这些是Odoo自动添加到每个新模型的保留字段。它们如下所示:
- id是模型中每个记录的唯一数字标识符。
- create_date和create_uid指定创建记录的时间和由谁创建它。
- write_date和write_uid确认记录的上次修改时间以及谁对其进行了修改。
- __last_update是一个实际上不存储在数据库中的帮助信息。它用于并发检查。
2.2.2.加入自动化测试
最佳编程实践包括自动测试你的代码。 这对动态语言更为重要,如 Python语言。 因为没有编译的步骤, 代码实际是解释运行的,所以无法确定有无语法错误的。 一个优秀的编辑器可以提前帮助我们发现这些问题, 但却不能帮助我们确保代码按照预期的执行,比如自动化测试。
Odoo支持使用两种方式来描述测试: 使用 YAML 数据文件或使用 Python 代码、基于 Unittest2 库。 YAML 测试是一种遗留下来的旧老版本, 不建议使用。 我们会更乐意使用 Python 测试并将添加基本的测试实例到我门的模块。
测试代码文件必须用test_开头 , 并且从tests/init.py文件导入。 但 测试的 目录(或 Python 的子模块)不应从模块的顶部的init.py导入, 因为仅在测试的执行的时候会自动被发现并装载。
测试必须放置在 tests/的 子目录中。 添加一个tests/init.py 文件,包含如下内容:
from . import test_todo
现在添加实际的测试代码放在tests/test_todo.py文件里:
# -*- coding: utf-8 -*-
from odoo.tests.common import TransactionCase
class TestTodo(TransactionCase):
def test_create(self):
"Create a simple Todo"
Todo = self.env['todo.task']
task = Todo.create({'name': 'Test Task'})
self.assertEqual(task.is_done, False)
这里增加了一种简单的测试例子来创建新的to-do任务来验证测试例子是否被执行? 测试例子的字段具有合适的默认值。
现在我们即将运行我们的测试。 当我们在安装模,可以添加 --test-enable选项。
该Odoo服务器将寻找一个tests/子目录的升级模块 并运行。如果有任何测试失败了,服务器日志都会在这里显示。