一:继承
在不改变底层对象的时候添加新的功能——这是通过继承机制来实现的,作为在现有对象之上的修改层,这些修改可以发生在所有级别:模型,视图和业务逻辑。不是直接修改现有模块,而是创建一个新模块以添加预期的修改。
1:扩展模型
Odoo 模型存在 Python 的模块之外, 在中间注册表那里。对于这个注册表,可以通过模型的方法使用self.env[<model name>]进入。 例如, 通过res.partner 模型获取对象的引用, 代码书写如下 self.env['res.partner']。
添加字段:
# -*- coding: utf-8 -*- from odoo import models, fields, api class Sub(models.Model): _inherit = 'Root' //通过_inherit属性来继承父模型 newCol=fields.XX.... //在下面添加新字段即可。
修改现有字段:
为了改变现有字段的属性,只需再次定义该字段,需要修改的属性显式重写即可,会保留未修改的所有其他未在此处明确使用的字段的属性。
添加方法:
添加新方法很简单:只需在继承类中声明新的函数。
修改方法:
扩展或更改现有逻辑,可以通过声明具有完全相同名称的方法来覆盖相应的方法。
新方法将替换前一个方法,它可以只是扩展继承类的代码,使用Python的super()方法来调用父方法。然后,可以在调用super()方法之前和之后,在原有逻辑周围添加新逻辑。
扩展模型的几种方式:
1)类继承:扩展类中没有_name属性,因为它继承了父类的_name。对现有模型的扩展, 添加新功能,都将添加到现有模型中,不会创建新模型。 因此,在odoo中,根据_name唯一确定这个模型时找到的就是扩展后的类。(如上面添加字段是类继承)
2)原型继承:我们想使用具有不同于父模型的值的_name属性,我们将获得一个新模型重用来自继承的特性,但是具有自己的数据库表和数据。其实就是把继承的类的功能特性拷贝衣服给新的模型使用,并不改变被继承模型。
复制意味着被继承的方法和字段也将在继承模型中可用。 对于字段,这意味着它们也将被创建并存储在目标模型的数据库表中。 原始(继承)和新(继承)模型的数据记录保持不相关。 只有定义是共享的。
3)代理继承(委托继承):使用_inherits属性,它允许一个模型以透明的方式包含其他模型。通过字典映射继承模型与字段的关系,并关联它们。(相当于成员变量:引用一个外部的类对象赋值给这个模型的一个成员,但是对象的值是保存在被引用的类的数据库表中的。但是在当前类通过成员修改了变量值的,则会同步到被引用的类的数据库表中)
优点是不需要在几个表之间重复数据结构,例如地址。 任何需要包含地址的新模型都可以将其委派给嵌入式合作伙伴模型。 如果在合作伙伴地址字段中引入了修改,则这些修改会立即提供给嵌入它的所有模型!
2:扩展视图
表单,列表和搜索视图是使用arch XML结构定义的。 要扩展视图,我们需要一种方法来修改这个XML。 这意味着需要定位XML中的元素位置,然后在这些位置引入修改。
对于XML,在其中定位元素的最好方法是使用XPath表达式。如果XPath表达式匹配到多个元素,只有第一个元素会被修改。 因此,表达式应该使用独特的属性以使其指定尽可能具体。 使用name属性是确保我们找到扩展点的确切xml元素的最简单方法。 因此,在我们的视图XML元素上定义name属性是很重要的。
Xpath表达式的格式:expr="//标签名[@属性]='属性值'":找到属性=属性值的标签位置。
下面是一个写在arch中的实现在is_done字段之前添加date_deadline字段的具体例子:
<xpath expr="//field[@name]='is_done'" position="before"> //expr属性值的意思是:找到<filed name="is_done"/>的标签 <field name="date_deadline" /> </xpath>
Odoo为此提供了快捷符号,因此大多数时候我们可以完全避免XPath语法。 我们仅使用要定位的元素的特定属性及定位类型相关信息就可以了。
<field name="is_done" position="before"> <field name="date_deadline" /> </field>
如果字段在同一视图中多次出现,则应始终使用XPath语法。
position属性是可选的:
after:将内容添加到父元素之中,匹配的节点之后。
before:添加内容在匹配节点之前。
inside(默认值):匹配节点内的追加内容。
replace:替换匹配的节点。如果使用空内容,它将删除该匹配的元素。
attributes:修改匹配元素的XML属性。在元素内容使用<attribute name =“attr-name”>实现给属性name设置新属性值attr-name。
1)扩展表单视图
<record id="view_form_模块名_inherited" model="ir.ui.view"> <field name="name">模块名_extension</field> <field name="model">模块.数据模型</field> <field name="inherit_id" ref="模块_模型.被继承的form表单name属性"/> <field name="arch" type="xml"> //在arch中进行扩展操作:定位—>插入/修改 <field name="定位标签位置" position="在标签的哪里进行扩展"> <field ......> //扩展内容 </field> </field> </record>
2)扩展列表视图
<record id="view_tree_模型名_inherited" model="ir.ui.view"> <field name="name">模型名 extension</field> <field name="model">模块.模型</field> <field name="inherit_id" ref="被继承的tree视图name名"/> <field name="arch" type="xml">//在arch中进行扩展 <field name="定位标签名" position="扩展位置"> <field ....进行扩展 /> </field> </field> </record>
3)扩展搜索视图
<record id="view_filter_模型名_inherited" model="ir.ui.view"> <field name="name"> extension</field> <field name="model">模型名</field> <field name="inherit_id" ref="被继承的filter视图名"/> <field name="arch" type="xml"> //下面进行定位、扩展举例 <field name="name" position="after"> <filter name="filter_my_tasks" string="My Tasks" domain="[('user_id','in',[uid,False])]" /> </field> </field> </record>
4)修改记录
对于记录:
<record id="x" model="y">
数据记录加载时,实际上对模型y执行了create或update操作︰ 如果记录x不存在,则创建它; 否则,更新原来对应的值。
修改菜单项:
< ! — — 修改菜单项--> <record id="菜单视图id" model="ir.ui.menu"> //进行修改 </record>
修改action_window
<record model="ir.actions.act_window" id="action菜单id"> //进行修改 </record>
二:模块数据
1:列表数据的导出
数据导出是tree视图的标准功能,不涉及编码。
只需在列表视图左侧复选框勾选需要导出的记录,然后点击列表上方“动作”下拉列表,选择“导出”。
在导出对话框中选择需要导出的列、导出的格式(一般选择CSV,勾选 导入兼容导出),然后点击“导出到文件”即可。
2:导入数据
在面板点击“导入”按钮,选择文件后载入。然后点击“验证”,检查文件内容的格式合法性,如果正常,则点击“导入”即可。
3:模块数据
模块使用数据文件,将其配置加载到数据库,可以通过CSV和XML文件完成。为了完整性,也可以使用YAML文件格式,但是它很少用于加载数据。
一个附加的限制是文件名必须与要加载数据的模型的名称匹配,这样系统才可以推断应该将数据导入相应的模型。
数据CSV文件的常见用法是加载到ir.model.access模型中的安全定义。
4:演示数据
一个模块在安装时,尽量事先定义一些演示数据,方便在安装后进行测试使用。
演示数据我们放在data目录下,命名为 xx.xx.csv或者xx.xx.xml
然后在manifest文件中的data属性进行配置。
5:XML
noupdate:重复数据加载时,将重写上次运行中加载的记录。 这意味着升级一个模块将覆盖在数据库内可能已经进行的任何手动更改。此重新导入行为是默认值,但可以更改,以便在升级模块时,某些数据文件记录保持不变。 这是通过<odoo>或元素的noupdate =“1”属性完成的。 这些记录将在安装addon模块时创建,但在后续模块升级中不会对其进行任何操作。
XML中定义记录:
每个<record>元素有两个基本属性id和model,并且包含为每列分配值的<field>元素。如前所述,id属性对应于记录的外部标识符,并且模型属性对应于要写入记录的目标模型。
设置字段值的几种方式如下:
1:<record>元素定义数据记录并包含<field>元素以设置每个字段的值,field元素的name属性标识要写入的字段,要写入的值是元素内容:字段的开始和结束标记之间的文本。
2:定义字段值的更精细的替代方法是eval属性:它评估一个Python表达式并将结果值分配给字段。
<field name="date_deadline" eval="(datetime.now() + timedelta(-1)).strftime('%Y-%m-%d')" />
3:<field>元素还有一个ref属性,用于使用外部标识符设置many-to-one字段的值。
<field name="user_id" ref="base.user_demo" />
XML中触发函数:
可以通过<function>元素,在其加载过程中执行方法。这可以用来建立演示和测试数据。
<function model="数据模型" name="数据模型中的方法" eval="参数" />
另一方式是触发工作流:
<workflow model="模型" ref="工作流实例" action="工作流信号:触发工作流" />