现在我们已经有了开发环境并了解了如何管理实例及数据库,现在让我们来学习下如何创建插件模块。
本章内容如下:
- 创建和安装模块
- 完成manifest文件
- 组织模块文件结构
- 添加模型
- 添加菜单及视图
- 添加访问控制
- 使用scaffold命令创建模型
在odoo中什么是模块(add-on module)?
除了框架代码,其他的代码都是以模块的形式组织起来的。这些模块可以随时安装和卸载。由于odoo用于各种规模的公司,每一个公司都有自己的业务流程。为了解决这一个问题,应用将应用拆分到不同的模块当中。这些模块在需要的时候才会被载入。我们可以随时启停这些功能。同一个应用也可以适配不同的需求。如下截图当中是以不同的应用分组,每一组第一个应用是主模块,其余的是属于其辅助模块。
在创建应用时应该创建各个功能的边界。这将非常有助于将应用程序划分为不同的附加模块。下面让我们开始构建自己的附加模块了。
创建和安装新应用
准备
在用的odoo实例
步骤
作为例子,我们这一章将创建一个管理图书的模块。
- 进入项目工程,创建本地目录。
$ cd ~/odoo-dev
$ mkdir local-addons
- 创建项目目录
$ mkdir local-addons/my_library
- 创建__init__.py文件
$ touch local-addons/my_library/__init__.py
- 创建__manifest__.py文件,并写入
{'name': 'My Library'}
- 将本地目录配置到odoo路径中。
$ odoo/odoo-bin --addons-path=odoo/addon/,local-addons/
如果我们在命令行中使用--save,那么相应的配置会存储的配置文件下次使用的时候就会更方便了。
6. 通过在APP页面刷新本地目录按钮,可以在列表当中查看到我们新建的项目。
- 安装
原理
odoo模块是包含着源代码及其他文件的目录。该文件夹名称通常是模块的技术名称。
manifest.py是Python的字典包含着模块儿的相关信息。
模块必须是可导入的,也就是说它必须具备__init__.py文件,即使这个文件时空的。
完成模块的__manifest__.py文件
准备
上一节内容
步骤
- 编辑__manifest__.py文件
'name': "My library",
'summary': "Manage books easily",
'description': """
Manage Library
==============
Description related to library.
""",
'author': "Your name",
'website': "http://www.example.com",
'category': 'Uncategorized',
'version': '13.0.1',
'depends': ['base'],
'data': ['views/views.xml'],
'demo': ['demo.xml'],
}
- 为模块选择一个icon图标,放置在static/description/icon.png中。
原理
- name: 模块的名称。
- summary: 模块的简单介绍。
- description: 模块的长介绍。通常是纯文本或者ReStructuredText (RST) 格式。
- author: 模块作者。
- website: 模块开发者的网站。
- category: 模块的分类。关于模块分类有一个标准的列表可供选择( https://github.com/odoo/ odoo/blob/13.0/odoo/addons/base/data/ir_module_category_ data.xml. )。当然你也可以定义自己的名称。
- version: 模块的版本。这通常用于odoo检测是否有新的版本需要升级。如果所输入的版本当中并不包含包含odoo的主版本(12.0,13.0,14.0),odoo将自动添加。
- depends: 这是当前模块所依赖的其他模块。即便模块不依赖于其他的模块儿的话,要么也应该输入base模块。在填写依赖的时候应注意依赖的顺序。
- data: 这是数据文件的列表。数据文件的路径是相对于模块的根路径的通常是xml及csv文件。也可以是yaml文件,这在第六章进行介绍。
- demo: 这关联模块儿的演示数据。
由于odoo主版本之间存在蛮多的不同一个模块儿,可能并不是用于其他的版本。
更多
在__manifest__.py的description中,也可以是单独的描述性文件。自odoo8之后,支持README文件或者其他的txt、rst、md扩展名的文件。或者直接展示在description/index.html的内容。
html文件将直接复写描述中的内容。
还有几个关键的key
- licence: 默认是LGPL-3协议。
- application: True/False,当前模块是否是核心模块。
- auto_install: True/False,代表是否在其所依赖的模块完成安装后自动安装。
- installable: True/False,代表当前模块是否可安装。
- external_dependencies: 一些模块可能依赖于Python或bin包。如果当前环境不具备,这些内容那么安装将停止。
- {pre_init, post_init, uninstall}_hook: 这里代表python的函数,分别是安装前,安装后,卸载时运行(详细将在第八章介绍)
还有几个特殊的key - price: 代表当前模块儿的售价,如果没有设置的话,那么代表免费。
- currency: 代表当前模块儿售价的单位。默认是EUR。
- live_test_url: 表示在线试用的链接。
- iap: 如果模块提供IAP服务,请填写IAP的key。
- images: images的路径,这些图片用于在odoo的app商城上展示。
组织模块文件结构
模块当中包含源代码及其他各种各样的文件,虽然我们可以随便放置,但是建议还是按照模块的结构进行规范化。
准备
假定我们的模块位于local-addons/my_ library,并包含了__manifest__.py,__init__.py文件。
- 创建目录及文件
$ cd local-addons/my_library
$ mkdir models
$ touch models/__init__.py
$ mkdir controllers
$ touch controllers/__init__.py
$ mkdir views
$ touch views/views.xml
$ mkdir security
$ mkdir wizard
$ touch wizard/__init__.py
$ mkdir report
$ mkdir data
$ mkdir demo
$ mkdir i18n
- 编辑__init__.py文件
from . import models
from . import controllers
from . import wizard
大部分目录结构式如下
my_library
├── __init__.py
├── __manifest__.py
├── controllers
│ └── __init__.py
├── data
├── demo
├── i18n
├── models
│ └── __init__.py
├── security
├── static
│ ├── description
│ └── src
│ ├─ js
│ ├─ scss
│ ├─ css
│ └ xml
├── report
├── wizard
│ └── __init__.py
└──views
└── __init__.py
原理
odoo中主要有三种类型的文件
- Python: .py文件,包含模型对象、业务逻辑
- Data文件: 定义在data及demo的key中。通常是XML及CSV文件。YAML文件也可以,他可以在模块加载的时候执行一些指令。对于实例而言,相较于xml的静态更新,yaml可以实现动态生成和更新记录。
- Web assets是JavaScript、CSS、SCSS及QWeb/HTML模板的集合。这些文件通常用于构建友爱页面以及管理用户的点击行为。还有一些被定义为XML的文件,去扩成主模板的功能。
模块文件通过如下方式进行组织:
- models/: 包含模块的模型对象定义及其业务逻辑。详见第四章应用模型
- views/: 包含定义用户交互的xml文件。包括动作、form视图、list视图等。建议每一个模型都有单独的xml文件。网站模板的文件名建议以_template后缀。后端视图将在第九章后端视图介绍,网站视图将在第十四章CMS开发介绍。
- data/: 包含模型的初始化数据。将在第六章管理模块数据介绍。
- demo/: 包含演示数据,用于测试。
- il8n/: 用于存放翻译文件。以.pot及.po文件为主。将在第十一章,国际化中进行介绍。
- security/: 包含权限控制的相关文件。包括ir.model.access.csv,xml文件(定义权限组及记录规则信息)。详见第十章权限控制。
- controllers/: 包含web网站的访问定义等业务逻辑。详见第十三章,web服务开发。
- static/: 网站相关的定义文件。这里的文件是允许非登录用户访问的。主要包括JavaScript、样式文件及images。这并不需要在manifest中引用,但需要在web模板中引用。详见第十四章,CMS开发。
- wizard/: 包含向导相关的文件。在odoo中,wizard用于保存中间数据。详见第八章,服务端开发-进阶。
- report/: odoo提供了生成pdf文件的特性。这里将用于生成的pdf报告的相关文件。详见第十二章,自动化、流程、邮件及打印。
当我们添加了新的文件后,不要忘记在__manifest__.py及__init__.py中进行引用。
添加模型
模型定了我们业务逻辑中的数据结构。
在这一章节当中,我们将添加图书模型。
准备
步骤
- 添加模型文件models/library_book.py
from odoo import models, fields
class LibraryBook(models.Model):
_name = 'library.book'
name = fields.Char('Title', required=True)
date_release = fields.Date('Release Date')
author_ids = fields.Many2many(
'res.partner',
string='Authors'
)
- 将新增文件添加到models/init.py
from . import library_book
- 编辑模块的初始化文件__init__.py
from . import models
- 通过页面UI或者命令行更新模块
现在模型已添加到数据库中了。可通过两种方式查看: - 通过odoo的页面,在菜单 Settings|Technical|Database Structure|models下查找目标模型。
- 进入数据库查看
$ psql test-13.0
test-13.0# d library_book;
原理
odoo中有自己的的Object Relations Mapping(ORM)框架。orm框架提供了对postgresql数据库的抽象。通过继承odoo的python类model,我们能够创建自己的模型。
模型有几个通用的以_为前缀的属性。最重要的是_name,它是模型的唯一标识。ORM框架生成数据库表也是基于这个属性。数据库表明是将name中的”.“替换为”_“。
我们新增了模型文件后,需要将其添加到同目录的__init__.py文件中。
添加菜单及视图
这节我们将实现页面菜单及视图的展示。
准备
步骤
菜单和视图都是以xml文件的形式存在。
- 创建xml文件以添加数据记录,views/library_book.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Data records go here -->
</odoo>
- 将新增的xml文件添加到__manifest__.py文件中
{
'name': "My Library",
'summary': "Manage books easily",
'depends': ['base'],
'data': ['views/library_book.xml'],
}
- 在library_book.xml中添加动作action(用于打开视图)
<record id='library_book_action' model='ir.actions.act_ window'>
<field name="name">Library Books</field>
<field name="res_model">library.book</field>
<field name="view_mode">tree,form</field>
</record>
- 添加菜单,并将action关联到菜单上
<menuitem name="My Library" id="library_base_menu" />
<menuitem name="Books" id="library_book_menu" parent="library_base_menu" action="library_book_action"/>
- 在library_book.xml中添加form视图
<record id="library_book_view_form" model="ir.ui.view">
<field name="name">Library Book Form</field>
<field name="model">library.book</field>
<field name="arch" type="xml">
<form>
<group>
<group>
<field name="name"/>
<field name="author_ids" widget="many2many_tags"/>
</group>
<group>
<field name="date_release"/>
</group>
</group>
</form>
</field>
</record>
- 添加tree(list)列表视图
<record id="library_book_view_tree" model="ir.ui.view">
<field name="name">Library Book List</field>
<field name="model">library.book</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="date_release"/>
</tree>
</field>
</record>
- 添加搜索视图
<record id="library_book_view_search" model="ir.ui.view">
<field name="name">Library Book Search</field>
<field name="model">library.book</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="author_ids"/>
<filter string="No Authors"
name="without_author"
domain="[('author_ids','=',False)]"/>
</search>
</field>
</record>
- 当一个新的模型添加到odoo中后,默认是没有访问权限的,这样菜单及视图也是看不到的。我们可以通过进入超级管理员模式去查看相关内容。
进入超级管理员模式
进入管理员模式后,右上角视图将变成
原理
数据文件可以放在模块目录中的任何位置,但约定是在视图/子目录中定义用户界面。通常,这些文件的名称基于模型的名称。在本例中,我们正在为图书馆.book模型,所以我们创建了 views/library_book.xml文件。
下一步是定义一个窗口操作。动作操作的目标模型定义在res_model中,name属性用于在用户打开动作时向用户显示标题。这些只是基本属性。窗口操作支持附加属性,从而可以更有效地控制视图的呈现方式,例如显示哪些视图、在可用记录上添加过滤器或设置默认值。在第9章后端视图中详细讨论了这些问题。
通常,数据记录是使用
相似,菜单是添加到ir.ui.menu模型中。我们也可以使用
- name: 菜单展示的内容
- action: 点击菜单后出发的动作
- sequence: 菜单的展示顺序
- parent: 代表当前菜单所属的父级菜单
- web_icon: 标识显示在菜单上的icon图标。这个只在企业版生效。
截止目前,我们还没有添加视图。但是如果我们更新模块,那么odoo将自动创建默认的视图。
所有的视图都是定义在ir.ui.view模型中的。主要属性如下: - name: 视图的标题。如果没写,odoo将视同模型的名称及视图的类型自动生成一个。
- model: 这是视图所属的模型。
- arch: 这是视图的架构。
Form视图是定义在
添加访问控制
在我们的前面提到的model、view、menu都涉及到权限控制。
在odoo中,权限是以组的形式展开的。用户属于某些权限组,通过对权限组进行授权实现对model、view、menu的控制。
准备
步骤
- 权限组,security/groups.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="group_librarian" model="res.groups">
<field name="name">Librarians</field>
<field name="users" eval="[(4, ref('base.user_ admin'))]"/>
</record>
</odoo>
- 通过security/ir.model.access.csv进行模型权限的配置
id,name,model_id:id,group_id:id,perm_read,perm_ write,perm_create,perm_unlink
acl_book,library.book default,model_library_book,,1,0,0,0
acl_book_librarian,library.book_librarian,model_library_ book,group_librarian,1,1,1,1
- 将以上文件添加到__manifest__.py中
# ...
'data': [
'security/groups.xml',
'security/ir.model.access.csv',
'views/library_book.xml'
],
# ...
原理
以上我们涉及了权限组的创建及对权限组进行模型授权
- security/groups.xml,定义了权限组,id为权限组的唯一标识,name是权限组的名称,users是权限组初始化时包含的用户。其实groups也是将权限组的记录写入res.groups模型中。
- ir.model.access.csv文件,定义了权限组对于模型的访问权限。其实就是将csv中的信息写入ir.model.access模型中。
使用scaffold命令创建模型
之前我们自己构建了模块的结构,但是其实odoo提供了简单构建模块的命令: scaffold。
准备
步骤
- 进入本地模块所在目录,如下:
$ cd ~/odoo-dev/local-addons
- 通过命令创建模块, scaffold 模块的技术名称
$ ~/odoo-dev/odoo/odoo-bin scaffold my_module
- 如下是命令自动创建的模块架构
$ tree my_module
my_module/
├── __init__.py
├── __manifest__.py
├── controllers
│ ├── __init__.py
│ └── controllers.py
├── demo
│ └── demo.xml
├── models
│ ├── __init__.py
│ └── models.py
├── security
│ └── ir.model.access.csv
└── views
├── templates.xml
└── views.xml
5 directories, 10 files
原理
scaffold是基于模板创建新模块的。
默认是创建在当前目录下,也可指定存储的目录。
$ ~/odoo-dev/odoo/odoo-bin scaffold my_module ~/odoo-dev/local- addons
odoo中是有两个模板的,处于/odoo/cli/templates目录中。一个是默认的模板,另一个主要用于网站主题。当然我们也可以使用我们自己的模板。通过-t指定模板存储的路径。
$ ~/odoo-dev/odoo/odoo-bin scaffold -t path/to/template my_ module