Alan Hou的个人博客

Odoo 17开发者指南第三章 创建Odoo插件模块

现在我们已经有了开发环境,并且知道如何管理Odoo服务端实例和数据库,接下来学习如何创建Odoo的插件模块。

本章的主要目标是掌握插件模块的结构以及添加组件的典型增量工作流程。本章中提到的各种组件将在后续章节中详细介绍。

一个Odoo模块可以包含多个元素:

本章中,我们将介绍以下内容:

技术要求

学习本章,读者需要安装好Odoo,并且已经完成了第一章 安装Odoo开发环境中各节。还应熟悉如何发现和安装额外的插件模块,可参照第二章 管理Odoo服务端实例

本章中使用的所有代码可以从GitHub仓库下载,地址是:https://github.com/alanhou/odoo-cookbook/tree/main/Chapter03。

什么是Odoo插件模块?

除框架代码外,Odoo的所有代码库都以模块形式组织。这些模块可以随时从数据库中安装或卸载。这些模块有两个主要目的:可以添加新的应用程序/业务逻辑,或者修改现有的应用程序。简单来说,在Odoo中,一切都始于模块,也终于模块。

Odoo提供各种业务解决方案,如销售、采购、POS、会计、制造、项目和库存。新建模块涉及为业务添加新功能或升级现有功能。

最新版本的Odoo在社区版和企业版中引入了众多新模块,包括会议室、待办事项以及几个WhatsApp相关的集成模块。

此外,该版本还带来了令人兴奋的新功能,如重新设计的用户界面、改进的搜索功能以及CRM、制造和电子商务的新功能。新版本还包括其他改进,如增强的性能、提升的安全性和更多的集成。

Odoo由各种规模的公司使用;每个公司都有不同的业务流程和需求。为解决这个问题,Odoo将应用程序的功能分成不同的模块。这些模块可以按需加载到数据库中。基本上,管理员可以随时启用/禁用这些功能。因此,同一软件可以根据不同的需求进行调整。查看下面的Odoo模块截图;列中的第一个模块是主应用程序,其他模块是为该应用程序添加额外功能而设计的。要获取按应用程序类别分组的模块列表,请访问Apps菜单并按类别分组:

图3.1 – 按类别分组的应用程序

如果你计划在Odoo中开发一个新应用程序,应该为各种功能设置边界。这将非常有助于将应用程序分成不同的插件模块。既然读者已经了解了Odoo插件模块的目的,就可以开始构建我们自己的模块了。

创建和安装一个新插件模块

本例中,我们将新建一个模块,可用于我们的Odoo实例中,并进行安装。

准备工作

首先,我们需要一个Odoo实例。

如果按照第一章 安装Odoo开发环境中的从源码轻松安装Odoo小节的步骤操作,Odoo应该位于~/odoo-dev/odoo目录中。为方便解释,我们将假设Odoo位于这个位置,当然也可以使用你喜欢的其他位置。

我们还需要添加我们自己Odoo模块的位置。为本例我们将使用odoo目录同级的local-addons目录,即~/odoo-dev/local-addons

可以将你自己的Odoo模块上传到GitHub并克隆到本地系统进行开发。

操作步骤

例如,在本章中,我们将创建一个小型的插件模块来管理公寓。

按以下步骤创建并安装一个新的插件模块:

  1. 切换工作目录,并创建一个放置自定义模块的目录:
  2. 为新模块选择技术名称,并创建目录。本例,我们将使用my_hostel

    模块的技术名称必须是有效的Python标识符。它必须以字母开头,并且只能包含字母、数字和下划线字符。模块名称最好只使用小写字母。
  3. 通过添加__init__.py文件使Python模块可导入:
  4. 添加一个最小的模块声明,以便Odoo可以将其检测为插件模块。在my_hostel文件夹中创建一个__manifest__.py文件,并写入以下内容:
  5. 启动Odoo实例,并将模块目录添加到插件路径中:

    如果在Odoo命令中添加了--save选项,插件路径将被保存到配置文件中。下次启动服务器时,如果没有提供插件路径选项,将使用这个路径。
  6. 在Odoo实例中启用新模块。使用admin账号登录Odoo,在About对话框中启用开发者模式,在顶部Apps菜单中选择更新应用列表。此时,Odoo应该识别到了我们的Odoo模块:


    图3.2 – 更新应用列表的对话框
  7. 在顶部选择Apps菜单,在右上角的搜索栏中删除默认的应用过滤器并搜索my_hostel。点击Activate按钮,完成安装。

工作原理

Odoo模块是一个包含代码文件和其他资源的目录。使用的目录名称是模块的技术名称。模块声明中的name键是它的标题。

__manifest__.py文件是模块声明。包含一个Python字典,里面有模块元数据,包括类别、版本、依赖的模块以及加载的数据文件列表。还包含有关插件模块的重要元数据,并声明应加载的数据文件。

本节中,我们使用了一个最小的声明文件,但在实际模块中,我们还需要其他重要的字段。在下一节完成插件模块的声明中讨论。

模块目录必须是Python可导入的,因此还需要一个__init__.py文件,空的也行。要加载模块,Odoo服务器中会进行导入。这将执行__init__.py文件中的代码,因此它充当了运行模块Python代码的入口。因此,通常包含导入语句来加载模块Python文件和子模块。

已知的模块可以直接使用--init-i选项从命令行安装。例如,如果想安装my_hostel应用程序,可以使用-i my_hostel。在新建数据库时,这个列表最初会在当时提供的插件路径中找到的模块。可以使用更新模块列表菜单在现有数据库中进行更新。

完成插件模块的声明

声明是Odoo模块的重要部分。

准备工作

我们应该有了一个包含__manifest__.py声明文件的模块。可以参考前面小节来实现这样的模块。

操作步骤

我们为插件模块添加一个声明文件和图标:

  1. 编辑模块的__manifest__.py文件,使其看起来像这样:
  2. 要对模块添加图标,请选择一个PNG图像并将其复制到static/description/icon.png

工作原理

清单文件中的内容是一个普通的Python字典,包含键和值。我们使用的示例声明文件包含相关性最高的键:

用于模块图标的图像为PNG文件static/description/icon.png

Odoo在主版本之间预计会有显著变化,因此如不进行转换和迁移工作,为一个主要版本构建的模块不太可能与下一个版本可能不太兼容。因此,确定模块的Odoo目标版本非常重要。

为了确保兼容性,我们需要遵循这些步骤:

  1. 首先检查安装是否成功。如果成功,则继续检查模块功能是否正常工作。
  2. 如果安装不成功,则需要根据收到的错误调整代码和功能逻辑。

更多内容

除了在模块声明中的长描述,还可以有一个单独的描述文件。从8.0版开始,可以用README文件(带.txt.rst.md扩展名)替代。否则,包含模块的description/index.html文件。

这个HTML描述将覆盖声明文件中定义的描述。

还有一些常用的键:

还有一些特殊键用于应用商店列表:

组织插件模块的文件结构

插件模块包含代码文件和其他资源,如XML文件和图片。对于大多数这些文件,我们可以自由选择将它们放置在模块目录的何处。

然而,Odoo对模块结构有一些约定,所以建议遵循这些约定。良好的代码可以提高可读性,简化维护,帮助调试,降低复杂性,并提升可靠性。这些适用于每一个新模块和所有新的开发项目。

准备工作

我们需要有一个仅包含__init__.py__manifest__.py文件的插件模块目录。本节中,假定这个目录是local-addons/my_hostel

如何操作…

按以下步骤为插件模块创建一个基本框架:

  1. 为代码文件创建目录:
  2. 编辑模块的顶层__init__.py文件,加载子目录中的代码:

这样我们会有一个包含最常用目录的结构,类似这样:

工作原理

Odoo插件模块可以有三种类型的文件:

  1. Python代码:通过__init__.py文件加载,其中导入了 .py 文件和代码子目录。包含Python代码的子目录也需要自己的__init__.py文件。
  2. 数据文件:需要在模块声明__manifest__.pydatademo键中声明待加载的文件,通常是用于用户界面、测试夹具数据和演示数据的XML和CSV文件。也可以有YAML文件,包含在模块加载时运行的一些过程指令,例如以编程方式生成或更新记录,而不是静态地在XML文件中。
  3. Web资产:如JavaScript代码和库、CSS、SASS和QWeb/HTML模板,这些文件用于构建UI部分并管理这些UI元素中的用户操作。它们通过声明文件的资产清单键声明,包含新文件和现有文件,将这些资产添加到Web客户端、小部件或网页。

插件文件在以下目录中进行组织:

添加新文件到模块时,记得在__manifest__.py文件(对于数据文件)或__init__.py文件(对于代码文件)中声明它们,否则这些文件将被忽略且不会加载。

添加模型

模型定义将由我们的业务应用程序使用的数据结构。本节展示如何向模块添加基本模型。模型确定数据库的逻辑结构以及数据如何存储、组织和操作。换句话说,模型是可以与其他表链接的信息表。模型通常表示业务概念,如销售订单、联系人或产品。

模块包含各种元素,如模型、视图、数据文件、网页控制器和静态web数据。

要创建一个公寓模块,我们需要开发表示公寓的模型。

准备工作

我们应有一个可用的模块。如果你按照本章中的第一个小节创建和安装一个新插件模块进行操作,就会有一个名为my_hostel的空模块。我们将使用它进行说明。

如何操作…

要添加一个新模型,我们需要添加一个描述它的Python文件,然后升级插件模块(如尚未安装)。使用的路径是相对于我们的插件模块位置(例如, ~/odoo-dev/local-addons/my_hostel/):

  1. 添加一个Python文件到models/hostel.py模块,代码如下:
  2. models/__init__.py模块中添加一个Python初始化文件,加载代码文件:
  3. 编辑模块的Python初始化文件加载models/目录:
  4. 通过命令行或用户界面的Apps菜单升级Odoo模块。如果在升级模块时仔细查看服务器日志,应该会看到以下行:

之后,应该在我们的Odoo实例中可以使用新的hostel.hostel模型。有两种方法可以检查模型是否已添加到数据库中。

首先,可以在Odoo用户界面中检查。启用开发者工具并打开菜单Settings | Technical | Database Structure | Models。在这里搜索hostel.hostel模型。

第二种方法是检查PostgreSQL数据库中的数据表。可以在数据库中搜索hostel_hostel表。在以下代码示例中,我们使用test-17.0作为数据库名称。但你可以在以下命令替换你的数据库名称:

工作原理

第一步是创建一个Python文件,在其中创建我们的新模块。

Odoo框架有自己的对象关系映射(ORM)框架。这个ORM框架在PostgreSQL数据库之上提供了一个抽象层。通过继承Odoo的Model类,我们可以创建自己的模型(表)。定义新模型时,它也会被添加到中央模型注册表中。这样其他模块可以更容易地修改它。

模型有一些前缀为下划线的通用属性。最重要的是_name,它提供了一个唯一的内部标识符,将在整个Odoo实例中使用。ORM框架将基于这个属性生成数据库表。在我们的示例中,我们使用了_name = ‘hostel.hostel’。基于这个属性,ORM将生成一个名为hostel_hostel的表。可以看到ORM框架会通过将_name属性中的.替换为_来创建表名。_description提供了模型的非正式名称,我们使用_name = ‘hostel.hostel’_description=’Information about hostel’_description=’Information about hostel’仅能以字母开头,不能用数字或特殊字符开头。

模型字段被定义为类属性。我们首先定义了一个Char类型的name字段。这个字段对于模型是很方便的,因为默认情况下,当其他模型引用时,它会用作记录描述。

我们还使用了一个关系字段示例——state_id。它定义了HostelState之间的一对多关系。

关于模型,还有很多内容,我们将在第四章 应用模型中深入讨论。

接下来,必须让我们的模块知道这个新的Python文件。这是通过__init__.py文件实现的。由于我们将代码放在models/子目录中,我们需要在之前的__init__.py文件中导入该目录,进而包含另一个__init__.py文件,导入其中的每个代码文件(本例中只有一个)。

通过升级模块来启用对Odoo模型的更改。Odoo服务器将处理模型类到数据库结构更改的翻译。

虽然这里没有提供示例,但业务逻辑也可以添加到这些Python文件中,方法是向模型类添加新方法或扩展现有方法,如create()write()。这将在第五章 基础服务端开发中介绍。

添加访问安全

添加新数据模型时,需要定义谁可以创建、读取、更新和删除记录。在创建全新应用程序时,这可能涉及定义新的用户组。因此,如果用户没有这些访问权限,Odoo将不会显示您的菜单和视图。在前面的小节中,我们通过将admin用户转换为超级用户来访问我们的菜单。完成本节后,读者将能够直接以admin用户身份访问Hostel模块的菜单和视图。

本节基于之前的Hostel模型,并定义了一个新的用户安全组,控制谁可以访问或修改Hostel记录。

准备工作

需要在前面小节中提供的实现hostel.hostel模型的插件模块,因为在本节中,我们将为其添加安全规则。使用我们插件模块位置的相对路径(例如,~/odoo-dev/local-addons/my_hostel/)。

如何操作…

本节中添加的安全规则如下:

需要执行以下步骤进行实现:

  1. 创建一个名为security/hostel_security.xml的文件,内容如下:
  2. 添加一个名为security/ir.model.access.csv的文件,内容如下:
  3. 将这两个文件添加到__manifest__.py中:

更新实例中的插件模块后,新定义的安全规则将生效。

工作原理…

我们提供了两个新的数据文件,并将它们添加到插件模块的声明中,以便在安装或更新模块时将它们加载到数据库中:

声明文件数据部分的文件顺序很重要。创建安全组的文件必须在列出访问权限的文件之前加载,因为访问权限定义依赖于组的存在。由于视图可能特定于某个安全组,我们建议将组的定义文件放在列表中以确保安全。

参见

本书有一章专门介绍安全。有关安全的更多信息,请参见第十章 安全访问

添加菜单项和视图

有了数据结构所需的模型,我们就需要一个用户界面,以便用户与之交互。菜单和视图在构建和提升用户体验方面起着至关重要的作用。从技术角度来看,菜单是动态用户界面组件,呈现一组结构化的选项或链接,通常允许用户访问应用程序中的各种功能、函数或内容区域。本节基于之前的Hostel模型,添加了一个菜单项,以显示带有列表和表单视图的用户界面。

准备工作

需要在前面小节中提供的实现hostel.hostel模型的插件模块。使用我们插件模块位置的相对路径(例如,~/odoo-dev/local-addons/my_hostel/)。

如何操作…

要添加视图,我们将向模块添加一个定义视图的XML文件。由于这是一个新模型,我们还必须为用户添加一个菜单选项,以便能够访问它。

对于模型,XML文件添加视图文件夹来创建视图、动作和菜单项。

请注意,以下步骤的顺序很重要,因为其中一些使用了在前面的步骤中定义的ID引用:

  1. 创建XML文件添加描述用户界面的数据记录,views/hostel.xml
  2. 将新的数据文件添加到插件模块声明文件__manifest__.py,在views/hostel.xml中添加以下内容:
  3. hostel.xml文件中添加打开视图的动作:
  4. 将菜单项添加到hostel.xml文件,使其对用户可见:
  5. hostel.xml文件中添加自定义表单视图:
  6. hostel.xml文件中添加自定义树状(列表)视图:
  7. 将自定义添加搜索选项到hostel.xml文件:

在Odoo中添加新模型时,用户默认没有任何访问权限。我们必须为新模型定义访问权限才能访问。本例中,我们尚未定义任何访问权限,因此用户无法访问我们的新模型。在没有访问权限的情况下,菜单和视图也不可见。所幸有一个快捷方式!通过切换到超级用户模式,可以在没有访问权限时看到我们应用的菜单。

以超级用户访问Odoo

通过将admin用户转换为superuser类型,可以绕过访问权限,无需授予默认访问权限即可访问菜单和视图。要将admin用户转换为超级用户,请启用开发者模式。完成此操作后,从开发者工具选项中单击成为超级用户选项。

开发者尽量在不成为超级用户的情况下进行所有操作;这将非常有助于深入学习Odoo。成为超级用户后,所有安全访问和记录规则检查将被绕过。

The following screenshot has been provided as a reference:

以下截图可供参考:



图3.3 – 启用超级用户模式的选项

成为超级用户后,菜单将具有条纹背景,如下图所示:

图3.4 – 启用超级用户模式

此时如尝试升级模块,应该能够看到一个新的菜单选项(可能需要刷新浏览器)。单击Hostel菜单将打开公寓模型的列表视图,如下图所示:

图3.5 – 访问公寓菜单

工作原理…

在底层,用户界面由存储在特殊模型中的记录定义。前两步是创建一个空的XML文件来定义要加载的记录,然后将它们添加到模块的待安装数据文件列表中。

数据文件可以放置在模块目录的任何位置,但按照惯例,用户界面通常在views/子目录中定义。这些文件的名称通常基于模型的名称。本例中,我们为hostel.hostel模型创建了用户界面,所以创建了views/hostel.xml文件。

下一步是定义一个窗口动作,以在网页客户端的主要区域显示用户界面。这个动作由res_model定义目标模型,name属性用于在用户打开动作时显示标题。这只是基本属性。窗口动作支持额外的属性,给予对视图渲染更多的控制,例如显示哪些视图、对可用记录添加过滤器或设置默认值。这些将在第九章 后端视图中详细讨论。

一般来说,数据记录使用<record>标签定义,我们在示例中为ir.actions.act_window模型创建了一个记录。这将创建窗口动作。

同样,菜单项存储在ir.ui.menu模型中,我们可以使用<record>标签创建这些菜单项。但在Odoo中有一个叫做<menuitem>的快捷标签,所以我们在示例中使用了这个标签。

以下是菜单项的主要属性:

此时,我们还没有在模块中定义任何视图。但是,如果在这个阶段升级模块,Odoo会自动动态创建这些视图。不过,我们肯定想控制视图的外观,因此在接下来的两步中,将创建一个表单视图和一个树状视图。

这两种视图都通过ir.ui.view模型上的记录定义。我们使用的属性如下:

表单视图用顶层<form>元素定义,其画布为两列网格。在表单中,<group>元素用于垂直排列字段。两个组元素产生两列中排列字段,这些字段使用<field>元素添加。字段根据其数据类型使用默认小部件,但可以使用widget属性来指定小部件。

树状视图更简单;它们用顶层<tree>元素定义,其中包含显示字段的<field>元素。

最后,我们添加了一个搜索视图,扩展右上角的搜索选项。在顶层<tag>中,我们可以有<field><filter>元素。Field元素是可以从搜索视图中输入内容进行搜索的附加字段。Filter元素是可以点击激活的预定义过滤条件。这些话题将在第九章 后端视图中详细讨论。

使用scaffold命令创建模块

在新建Odoo模块时,需要设置一些样板代码。为快速启动新模块,Odoo提供了scaffold命令。

本节展示如何使用scaffold命令新建模块,它为要使用的目录放置文件的骨架。

准备工作

我们将在自定义模块目录中创建新的插件模块,因此我们需要安装Odoo以及要有一个自定义模块目录。假定Odoo安装在~/odoo-dev/odoo,并且我们的自定义模块放在~/odoo-dev/local-addons目录中。

操作步骤

我们将使用scaffold命令创建样板代码。按以下步骤使用scaffold命令创建新模块:

  1. 将工作目录切换为我们希望模块所在的位置。可以是您选择的任何目录,但它需要在插件路径中才可用。按照我们在前一节中选择的目录,应如下所示:

     
  2. 为新模块选择一个技术名称,并使用scaffold命令创建它。本例我们将选择my_module
  3. 编辑__manifest__.py默认模块声明并更改相就值。至少需要更改name键中的模块标题。

生成的插件模块应如下所示:

现在应编辑生成的各个文件,并使它们适应新模块的用途。

工作原理…

scaffold命令根据模板创建新模块的骨架。

默认情况下,在当前工作目录中新建模块,但我们可以指定目录来创建模块,以额外参数传递。

考虑以下示例:

使用的是默认模板,但也用theme模板创建网站主题。要指定模板,可以使用-t选项。我们也可以使用模板目录的路径。

这意味着我们可以将自己的模板与scaffold命令一起使用。内置模板位于Odoo子目录/odoo/cli/templates中。要使用我们自己的模板,可以用如下命令:

默认,Odoo在/odoo/cli/templates目录中有两个模板。一个是默认模板,另一个是主题模板。但可以自建模板或使用-t选项,如上例所示。

退出移动版