现在我们已经有了开发环境并了解了如何管理实例及数据库,现在让我们来学习下如何创建插件模块。

本章内容如下:

  • 创建和安装模块
  • 完成manifest文件
  • 组织模块文件结构
  • 添加模型
  • 添加菜单及视图
  • 添加访问控制
  • 使用scaffold命令创建模型

在odoo中什么是模块(add-on module)?

除了框架代码,其他的代码都是以模块的形式组织起来的。这些模块可以随时安装和卸载。由于odoo用于各种规模的公司,每一个公司都有自己的业务流程。为了解决这一个问题,应用将应用拆分到不同的模块当中。这些模块在需要的时候才会被载入。我们可以随时启停这些功能。同一个应用也可以适配不同的需求。如下截图当中是以不同的应用分组,每一组第一个应用是主模块,其余的是属于其辅助模块。

在创建应用时应该创建各个功能的边界。这将非常有助于将应用程序划分为不同的附加模块。下面让我们开始构建自己的附加模块了。

创建和安装新应用

准备

在用的odoo实例

步骤

作为例子,我们这一章将创建一个管理图书的模块。

  1. 进入项目工程,创建本地目录。
$ cd ~/odoo-dev
$ mkdir local-addons
  1. 创建项目目录
$ mkdir local-addons/my_library
  1. 创建__init__.py文件
$ touch local-addons/my_library/__init__.py
  1. 创建__manifest__.py文件,并写入
{'name': 'My Library'}
  1. 将本地目录配置到odoo路径中。
$ odoo/odoo-bin --addons-path=odoo/addon/,local-addons/

如果我们在命令行中使用--save,那么相应的配置会存储的配置文件下次使用的时候就会更方便了。

6. 通过在APP页面刷新本地目录按钮,可以在列表当中查看到我们新建的项目。

  1. 安装

原理

odoo模块是包含着源代码及其他文件的目录。该文件夹名称通常是模块的技术名称。

manifest.py是Python的字典包含着模块儿的相关信息。

模块必须是可导入的,也就是说它必须具备__init__.py文件,即使这个文件时空的。

完成模块的__manifest__.py文件

准备

上一节内容

步骤

  1. 编辑__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'],
}
  1. 为模块选择一个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文件。

  1. 创建目录及文件
$ 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
  1. 编辑__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中进行引用。

添加模型

模型定了我们业务逻辑中的数据结构。

在这一章节当中,我们将添加图书模型。

准备

步骤

  1. 添加模型文件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'
)
  1. 将新增文件添加到models/init.py
from . import library_book
  1. 编辑模块的初始化文件__init__.py
from . import models
  1. 通过页面UI或者命令行更新模块

    现在模型已添加到数据库中了。可通过两种方式查看:
  2. 通过odoo的页面,在菜单 Settings|Technical|Database Structure|models下查找目标模型。
  3. 进入数据库查看
$ 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文件的形式存在。

  1. 创建xml文件以添加数据记录,views/library_book.xml
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Data records go here -->
</odoo>
  1. 将新增的xml文件添加到__manifest__.py文件中
{
'name': "My Library",
'summary': "Manage books easily",
'depends': ['base'],
'data': ['views/library_book.xml'],
}
  1. 在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>
  1. 添加菜单,并将action关联到菜单上
<menuitem name="My Library" id="library_base_menu" />
<menuitem name="Books" id="library_book_menu" parent="library_base_menu" action="library_book_action"/>
  1. 在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>
  1. 添加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>
  1. 添加搜索视图
<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>
  1. 当一个新的模型添加到odoo中后,默认是没有访问权限的,这样菜单及视图也是看不到的。我们可以通过进入超级管理员模式去查看相关内容。

进入超级管理员模式

进入管理员模式后,右上角视图将变成

原理

数据文件可以放在模块目录中的任何位置,但约定是在视图/子目录中定义用户界面。通常,这些文件的名称基于模型的名称。在本例中,我们正在为图书馆.book模型,所以我们创建了 views/library_book.xml文件。

下一步是定义一个窗口操作。动作操作的目标模型定义在res_model中,name属性用于在用户打开动作时向用户显示标题。这些只是基本属性。窗口操作支持附加属性,从而可以更有效地控制视图的呈现方式,例如显示哪些视图、在可用记录上添加过滤器或设置默认值。在第9章后端视图中详细讨论了这些问题。

通常,数据记录是使用标记定义的,并将记录添加到模型ir.actions.action_window的数据库表中。

相似,菜单是添加到ir.ui.menu模型中。我们也可以使用的简化标签。

下面是菜单的主要属性:

  • name: 菜单展示的内容
  • action: 点击菜单后出发的动作
  • sequence: 菜单的展示顺序
  • parent: 代表当前菜单所属的父级菜单
  • web_icon: 标识显示在菜单上的icon图标。这个只在企业版生效。

    截止目前,我们还没有添加视图。但是如果我们更新模块,那么odoo将自动创建默认的视图。

    所有的视图都是定义在ir.ui.view模型中的。主要属性如下:
  • name: 视图的标题。如果没写,odoo将视同模型的名称及视图的类型自动生成一个。
  • model: 这是视图所属的模型。
  • arch: 这是视图的架构。

    Form视图是定义在元素中的,通过进行组织展示的元素。

    Tree视图定义在,Search视图定义在。

添加访问控制

在我们的前面提到的model、view、menu都涉及到权限控制。

在odoo中,权限是以组的形式展开的。用户属于某些权限组,通过对权限组进行授权实现对model、view、menu的控制。

准备

步骤

  1. 权限组,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>
  1. 通过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
  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。

准备

步骤

  1. 进入本地模块所在目录,如下:
$ cd ~/odoo-dev/local-addons
  1. 通过命令创建模块, scaffold 模块的技术名称
$ ~/odoo-dev/odoo/odoo-bin scaffold my_module
  1. 如下是命令自动创建的模块架构
$ 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

【odoo14】第三章、创建插件的更多相关文章

  1. Rxjava2实战--第三章 创建操作符

    Rxjava2实战--第三章 创建操作符 Rxjava的创建操作符 操作符 用途 just() 将一个或多个对象转换成发射这个或者这些对象的一个Observable from() 将一个Iterabl ...

  2. [ABP教程]第三章 创建、更新和删除图书

    Web应用程序开发教程 - 第三章: 创建,更新和删除图书 关于本教程 在本系列教程中, 你将构建一个名为 Acme.BookStore 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以 ...

  3. Maven for Eclipse 第三章 ——创建和导入 Maven 项目

    这一章主要介绍 Maven 项目的结构,它的构建的架构,主要涵盖了必需的主题,最后将学习如何创建一个简单的 Maven 项目.这章主要包括以下部分. Maven 项目的结构 POM 文件(Projec ...

  4. [HeadFirst-HTMLCSS学习笔记][第三章创建网页]

    一些基本元素 以下元素都可以用CSS变得更好看 q,<blockquote>,<em>,<br>, <strong>,ol ,ul,li,pre,cod ...

  5. kubernetes第三章--创建harbor私有镜像库

  6. [转]TEC1401.Report开发技术总结 - 第三章 使用Oracle Reports开发报表-创建一个分组报表(2/4)

    本文转自:http://blog.csdn.net/deepsea_allen/article/details/53900284 第三章   创建一个分组报表 1.     建立数据模型 数据模型用于 ...

  7. Gradle 1.12用户指南翻译——第三十九章. IDEA 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  8. Gradle 1.12用户指南翻译——第三十七章. OSGi 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  9. Gradle 1.12用户指南翻译——第三十四章. JaCoCo 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

随机推荐

  1. conda 命令笔记

    1.虚拟环境 conda -V # 查看当前conda 版本 conda update conda # 更新conda conda env list 查看当前已有的虚拟环境 conda create ...

  2. 实战交付一套dubbo微服务到k8s集群(1)之Zookeeper部署

    基础架构 主机名 角色 IP地址 mfyxw10.mfyxw.com K8S代理节点1,zk1 192.168.80.10 mfyxw20.mfyxw.com K8S代理节点2,zk2 192.168 ...

  3. js的变量,作用域,内存

    一,基本类型和引用类型的值基本类型的值是按值访问的,引用类型的值是保存在内存中的对象1,动态的属性 只有引用类型的值可以添加属性方法 不能给基本类型添加属性和方法2,复制变量值 复制基本类型的值,两个 ...

  4. IDEA 安装常用操作二

    一.IDEA启动慢,因为启动时编译.缓存.创建索引等,如果断电等启动异常,可以让缓存索引失效 迁移IDEAD时,可以找到config.system等文件夹, 随着时间使用越久,空间占用越大,另外觉得浪 ...

  5. JPG学习笔记3(附完整代码)

    #topics h2 { background: rgba(43, 102, 149, 1); border-radius: 6px; box-shadow: 0 0 1px rgba(95, 90, ...

  6. TypeScript & WebAssembly

    TypeScript & WebAssembly WASM (module (func (param $lhs i32) (param $rhs i32) (result i32) local ...

  7. SVG animation(text, background)

    SVG animation(text, background) demo https://www.happyelements.com/ LICEcap bug Giphy 低帧率 gif https: ...

  8. react 异步的setState

    import React from "react"; class App extends React.Component { state = { a: 0 }; component ...

  9. 心之所向·智慧绽放丨NGK区块链赋能实体经济论坛圆满落幕

    据外媒报导,近日,由NGK主办的"NGK区块链赋能实体经济论坛"于英国伦敦的威斯敏斯特中央大厅圆满落幕.大会现场到来了NGK北美市场领导人.区块链行业的专业人士.NGK英国社区代表 ...

  10. 用Python实现一个“百度翻译”

    import requests import json s = input("请输入你要翻译的内容:") headers = {"User-Agent":&qu ...