在这篇文章中,我们要学习odoo中的继承。如果你是一个新手,看到这里的时候,不要觉得继承是个高大上的东西,也不要去想这里的继承和之前学的Java/Python这些里的继承有什么不同,有什么相同的。你只需要知道,我们现在是要将一块块积木拼起来。这篇文章就是说明怎么把积木拼起来的。
模块安装前的依赖
在每个模块的 __manifest__.py 文件中,都会看到一个键叫 depends :
'depends': ['base'],
depends 的值是一个列表,他说明了,当前模块需要依赖哪些已有模块。就像拼积木的时候,我们不可能直接拼出空中花园吧。说明书上一定说了,这个花园应该拼在第四层,但是必须先拼出第三层。这个第三层就是第四层的依赖。同样的,第三层需要依赖第二层,第二层需要依赖第一层(地基)。 当然,这是其中的一种情况,还有一种情况需要使用依赖,就是我们觉得第三层拼的太丑了,我们要给第三层改变一下积木颜色,这时候,我们也是要依赖第三层,在第三层的基础上作修改的。
在odoo 的__manifest__.py 文件中,depends 说明了依赖关系,使用依赖的情况,总结如下:
- 在已有模块上,拓展出新的外观或功能
- 改变已有模块的外观或功能
继承可以在已有模块中做,也可以新建一个模块,在新建模块中做。
建议:不要在 odoo 提供的模块中修改文件,如果需要改动odoo提供的模块,要新建一个模块,对odoo提供的模块进行二次开发。如果您想要对您自己开发的模块作继承,可以在模块中修改,也可以新建新的模块对其继承。
本文中的继承虽是对我们自己开发的模块作继承,但我还是新建模块来举例,创建新的模块 zerone_inherit 。并在 __manifest__.py 文件中,配置依赖项 depends:
{
'name': "zerone_inherit",
'summary': """
继承学习
""",
'description': """
继承学习:
对书架模型进行继承
""",
'author': "zerone",
'category': 'tools',
'version': '0.1',
'depends': ['zerone_books'],
'data': [
],
'qweb': [
],
'installable': True,
'application': True,
}
模块中模型继承
模型中的继承,通常是 增加新的字段,改变已有字段的属性,增加新的模型方法,改变已有的模型方法。
在对模型的继承时,需要使用到 _inherit 。 (关于模型继承 _inherit / _name / _inherts 的区别,请点击这里)
1 增加新的字段
本文中,我们对 zerone.shelf 模块作继承,为其增加一个新的字段 description ,用来表示对当前书架的描述说明。
创建 新的文件 zerone_inherit/models/inherit_zerone_shelf.py
from odoo import api, fields, models, _
class ZeroneShelf(models.Model):
_inherit = "zerone.shelf"
description = fields.Text(string="书架说明")
像上面这样,就为 zerone.shelf 增加了新的字段。重启,刷新本地模块列表,找到并安装zerone_inherit模块,然后在数据库管理工具中,执行下面语句,可以看到,zerone_shelf 表中就有了新的字段description 。如何将其添加到视图中,请看视图继承部分。
select * from zerone_shelf;
2 修改已有字段属性
我们继续对zerone.shelf 模型作继承,将字段name的string 属性由【书架名称】改为【名称】,并为其增加长度限制。
from odoo import api, fields, models, _
class ZeroneShelf(models.Model):
_inherit = "zerone.shelf"
description = fields.Text(string="书架说明")
name = fields.Char(string="名称", size=32)
像上面这样,将原有代码拿过来,为string 重新赋值即可修改显示名,继续添加size属性,即可设置长度。
3 增加新的模型方法
同增加字段一样,在继承的模型中,直接增加方法即可
class ZeroneShelf(models.Model):
_inherit = "zerone.shelf"
description = fields.Text(string="书架说明")
name = fields.Char(string="名称", size=32)
def say_hello(self):
print("hello")
4 修改已有模型方法
修改已有方法,一种是将原有方法copy过来,在方法中进行修改;一种是自我调用。下面说一下自我调用,实际就是重写方法,拿重写 write 方法举例:
class ZeroneShelf(models.Model):
_inherit = "zerone.shelf"
description = fields.Text(string="书架说明")
name = fields.Char(string="名称", size=32)
def say_hello(self):
print("hello")
def write(self, vals):
# 说明: 在 super 之前,可以通过 self 获取到更新前的数据
print("更新前", self.name)
# 说明: 如果想在更新见对数据做一些调整,可以处理 vals, 例如:
# vals["name"] = 'ceshi' + vals["name"]
res = super(ZeroneShelf, self).write(vals)
# 说明: 在 super 之后,通过 self 只能获取到到更新后的数据;
print("更新后", self.name)
return res
上面的例子中,对 write 方法进行了重写,在编辑已有记录的时候,会体现出来。方法中的第一句,实现了重写前的功能调用,第二句进行了新write方法的打印输出。当然,你可以根据两行代码的顺序决定,先调用功能再输出,还是先输出再调用功能。
模块中视图继承
1 增加新的字段
创建 新的文件 zerone_inherit/views/inherit_zerone_shelf.xml 并在 __manifest__.py文件中引用
在为视图作增加显示字段时,要在xml文件中,定义新的record ,id 必须是唯一的,inherit_id 使用ref 指向说明,格式:被继承视图所在模块名.被继承视图id。继承一般使用 <xpath> 标签,对已有字段定位,使用 position 决定将新的字段放在已有字段的何处,position常用的值,一般有 before:放在前面 ,after: 放在后面,repalce:将其替换,inside:放在标签中。
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="inherit_view_zerone_shelf_tree" model="ir.ui.view">
<field name="name">inherit.zerone.shelf.tree</field>
<field name="model">zerone.shelf</field>
<field name="inherit_id" ref="zerone_books.view_zerone_shelf_tree"/>
<field eval="1" name="priority"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='book_ids']" position="before">
<field name="description"/>
</xpath>
</field>
</record>
<record id="inherit_view_zerone_shelf_form" model="ir.ui.view">
<field name="name">inherit.zerone.shelf.from</field>
<field name="model">zerone.shelf</field>
<field name="inherit_id" ref="zerone_books.view_zerone_shelf_form"/>
<field eval="1" name="priority"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='capacity_rate']" position="after">
<field name="description"/>
</xpath>
</field>
</record>
</odoo>
2 删除已有字段
删除视图中已有字段,我们拿book_ids 字段举例, 可以这么做:
<record id="inherit_view_zerone_shelf_tree" model="ir.ui.view">
<field name="name">inherit.zerone.shelf.tree</field>
<field name="model">zerone.shelf</field>
<field name="inherit_id" ref="zerone_books.view_zerone_shelf_tree"/>
<field eval="1" name="priority"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='book_ids']" position="before">
<field name="description"/>
</xpath>
<xpath expr="//field[@name='book_ids']" position="replace">
</xpath>
</field>
</record>
需要注意的是,我们在第7行,根据book_ids 定位,在他前面增加了 description 字段,但我们又在第10行,将 book_ids 移除了。那么这种情况,移除的代码,必须要在后面,这是有顺序的,如果把移除部分的代码放在前面,就会报错,因为我拿来定位的字段先被移除了,找不到了,所以就无法插入 description 。
上面的操作的操作效果,也可以使用下面的代码实现:
<record id="inherit_view_zerone_shelf_tree" model="ir.ui.view">
<field name="name">inherit.zerone.shelf.tree</field>
<field name="model">zerone.shelf</field>
<field name="inherit_id" ref="zerone_books.view_zerone_shelf_tree"/>
<field eval="1" name="priority"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='book_ids']" position="replace">
<field name="description"/>
</xpath>
</field>
</record>
3 修改字段属性
修改属性的情况也有很多,大家可以先全剧搜索odoo中代码,看odoo是怎么实现的。我在这里只做个简单的继承例子,之zerone_books模块中,zerone_shelf 表单视图中的 book_ids 有一个属性是 readonly = "1",表示只读/不可编辑。我们在zerone_inherit 模块中将其改为可编辑,使用 position = "attributes":
<record id="inherit_view_zerone_shelf_form" model="ir.ui.view">
<field name="name">inherit.zerone.shelf.from</field>
<field name="model">zerone.shelf</field>
<field name="inherit_id" ref="zerone_books.view_zerone_shelf_form"/>
<field eval="1" name="priority"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='capacity_rate']" position="after">
<field name="description"/>
</xpath>
<xpath expr="//field[@name='book_ids']" position="attributes">
<attribute name="readonly">0</attribute>
</xpath>
</field>
</record>
这篇文章,比较空洞,因为关于继承的细节太多了,这篇文章中,没能把所有的情况举出。如果朋友们有什么地方没有明白,清在下面留言,咱们新开文章说明。