Odoo开发之模块继承(一)

Posted by Ethan on 2019-09-15

原模型继承

为模型添加字段

  • 通过 Python 类来新建模型,继承模型同样是通过 Python 以及 Odoo 自有的继承机制,即_inherit类属性。
  • 该属性标明所继承的模型。新的类继承父 Odoo 模型的所有功能,仅需声明要做修改的部分。
  • 添加library_member/models/library_book.py文件来继承原模型,首先创建__init__.py文件来导入该文件:
  1. 添加library_member/init.py文件来导入 models 子文件夹
1
from . import models
  1. 添加library_member/models/init.py文件子文件夹中的代码文件:
1
from . import library_book
  1. 创建library_member/models/library_book.py文件来继承library.book模型:
1
2
3
4
5
6
7
from odoo import api, fields, models


class Book(models.Model):
_inherit = 'library.book'

is_available = fields.Boolean('Is Available?')
  • 使用_inherit类属性来声明所继承模型。注意我们并没有使用到其它类属性,甚至是_name也没使用。除非想要做出修改,否则不需要使用这些属性。
  • 可以把这个想成是对模型定义的一个引用,在原处做了一个修改。
  • 可以添加字段、修改已有字段、修改模型类属性甚至是包含业务逻辑的方法。
  • 要在数据表中添加新的模型字段需要安装该模块。

修改已有字段

在继承模型时,可对已有字段叠加修改,也就是说仅需定义要增加或修改的字段属性。
我们将对原来创建的library_app模块的Book模型做两处简单修改:

  • isbn字段添加一条提示,说明同时支持10位数的 ISBN
  • publisher_id字段添加数据库索引,以提升搜索效率
  • 编辑library_member/models/library_book.py文件,并在library.book模型中添加如下代码:
1
2
3
4
class Book(models.Model):
...
isbn = fields.Char(help="Use a valid ISBN-13 or ISBN-10.")
publisher_id = fields.Many2one(index=True)

修改视图和数据

模块中视图和其它数据构件也可通过继承来修改。就视图而言,通常需要添加功能。视图的展示结构在arch字段中使用XML定义。

继承视图

  • 表单、列表和搜索视图通过arch XML结构定义。要继承视图,就要一种修改XML的方式,也即定位XML元素然后对该处进行修改。
  • 视图继承的XML记录和普通视图中相似,多一个inherit_id属性来引用所要继承的视图。
  • 下面我们来继承图书视图并添加is_available字段。
    • 首先要查找待继承的视图的XML ID,通过"设置 > 技术 > 用户界面 > 视图"菜单来查看。
    • 图书表单的XML IDlibrary_app.view_form_book。然后还要找到要插入修改的XML元素,我们在ISBN字段之后添加Is Available?通常通过name属性定位元素,此处为<field name="isbn"/>
  • 我们添加views/book_view.xml文件来继承Partner视图,加入如下内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0"?>
<odoo>
<record id="view_form_book_extend" model="ir.ui.view">
<field name="name">Book: add Is Available? field</field>
<field name="model">library.book</field>
<field name="inherit_id" ref="library_app.view_form_book" />
<field name="arch" type="xml">
<!--field name="isbn" position="after">
<field name="is_available" />
</field-->
<xpath expr="//field[@name='isbn']" position="after">
<field name="is_available" />
</xpath>
</field>
</record>
</odoo>
  • inherit_id记录字段通过ref属性指向继承视图的外部标识符
  • 视图使用XML定义并存储在结构字段arch中。要继承一个视图,先定位要扩展的节点,然后执行要做的操作,如添加XML元素。
  • 定位节点的最简单方法是使用唯一标识属性,
  • 通常是name。然后添加定位属性,声明要做的修改。本例中继承节点是name="isbn"元素,修改是在选定元素后加一段XML
1
2
3
<field name="isbn" position="after">
<!-- 此处添加修改内容 -->
</field>

一旦XML节点被选为继承点,需要指明要执行的继承操作。这通过position属性实现:

  • inside(默认值):在所选节点内添加内容,这一节点应是<group><page>一类的容器
  • after:在选定节点之后向父节点添加内容
  • before:在选定节点之前向父节点添加内容
  • replace:替换所选节点。若使用空元素则会删除该元素。Odoo 之后还允许使用其它标记来包裹元素,通过在内容中使用$0来表示被替换的元素。
  • attributes:修改匹配元素属性值。内容中应包含带有一个或多个<attribute name="attr-name">value</attribute>元素。
  • <attribute name="invisible">True</attribute>,若不带内容,如<attribute name="invisible"/>attribute会从所选元素中删除。
  • 通过position="replace"可删除XML元素,但应避免这么做。这么做会破坏其它依赖所删除节点、将其作为占位符添加元素的模块。一个替代方案是,让该元素不可见。
  • 除了attributes定位,上述定位符可与带position="move"的子元素合并。效果是将子定位符目标节点移到父定位符目录位置。
    例如:
1
2
3
<field name="target_field" position="after">
<field name="my_field" position="move"/>
</field>

其它视图类型,如列表和搜索视图,也有’arch`字段,可以表单视图同样的方式被继承。

使用XPath选取继承点

  • 有时可能没有带唯一值的属性来用作XML节点选择器。在所选元素没有name属性时可能出现这一情况,如<group><notebook><page>视图元素。
  • 另外就是有多个带有相同name属性的元素,比如在看板 QWeb 视图中相同字段可能在同一XML模板中被多次包含。
  • 在这些情况下我们就需要更高级的方式来定位待扩展XML元素。定位XML中元素的一种自然方式是XPath表达式。
  • 以定义的 Book 表单视图为例,定位<field name="isbn">元素的 XPath 表达式是//field[@name]='isbn'。该表达式查找name属性等于isbn<field>元素。
    前一部分对图书表单视图继承的XPath写法是:
1
2
3
<xpath expr="//field[@name='isbn']" position="after">
<field name="is_available" />
</xpath>

如果XPath表达式匹配到了多个元素,仅会选取第一个作为扩展目录。所以表达式应越精确越好,使用唯一属性。
name属性最易于确保找到精确元素作为扩展点,因此在创建视图XML元素时添加唯一标识符就非常重要。

修改数据

  • 普通数据记录不同于视图,它没有XML arch结构,也不能使用XPath来进行扩展。但还是可以通过替换字段值来进行修改。
    <record id="x" model="y">数据加载元素实际是对 y 模型进行插入或更新操作。
  • 若不存在记录 x,则被创建,否则被更新/覆盖。其它模块中的记录可通过<module>.<identifier>全局标识符访问,因此可以在我们的模块中重写其它模块中已写入的数据。
  • 点号是保留符号,用于分隔模块名和对象标识符,所以在标识符名中不要使用点号,而应使用下划线字符。
  • 举个例子,我们将User安全组的名称修改为Librarian,对应修改library_app.library_group_user记录。添加library_member/security/library_security.xml并加入如下代码:
1
2
3
4
5
6
<odoo>
<!-- Modify Group name -->
<record id="library_app.library_group_user" model="res.groups">
<field name="name">Librarian</field>
</record>
</odoo>

这里我们使用了一个<record>元素,仅写name字段。可以认为这是对所选字段的一次写操作。