10、继承
一、模型继承
模块继承
Odoo提供了两种继承机制以模块化方式扩展现有模型:
第一种个继承机制允许模块修改在另一个模块中定义的模型:
- 添加字段到模型中
- 重写模型上定义的字段
- 添加约束到模型中
- 添加方法到模型中
- 重写现有模型中的方法
链接:https://www.odoo.com/documentation/10.0/reference/orm.html#inheritance-and-extension
Odoo提供了3种继承机制以模块化方式扩展现有模型:
- 从现有的模型中创建一个新模型,向副本中添加新信息,但原样保留原始模块
- 扩展在其他模块中定义的模型,取代以前的版本
- 委托一些模型的字段给它所包含的记录
经典继承(Classical inheritance):
当我们一起使用 _inherit和_name属性时,Odoo创建了一个新的模型,使用现有的模型(通过_继承提供)作为基础。新模型从其基础上获取所有字段、方法和元信息(默认值和al)。
class Inheritance0(models.Model): _name = 'inheritance.0' name = fields.Char() def call(self): return self.check("model 0") def check(self, s): return "This is {} record {}".format(s, self.name) class Inheritance1(models.Model): _name = 'inheritance.1' _inherit = 'inheritance.0' def call(self): return self.check("model 1")
并使用它们:
a = env['inheritance.0'].create({'name': 'A'}) b = env['inheritance.1'].create({'name': 'B'}) a.call() b.call()
会产生:
"This is model 0 record A" "This is model 1 record B"
第二个模型继承了第一个模型的检查方法和名称字段,但是重写了call方法,就像使用标准Python继承时一样。
扩展(Extension)
当使用_inherit但省略_name时,新模型将替换现有的模型,本质上是在原来的位置进行扩展。这有助于向现有模型(在其他模块中创建)添加新的字段或方法,或定制或重新配置它们(例如更改它们的默认排序顺序)class Extension0(models.Model): _name = 'extension.0' name = fields.Char(default="A") class Extension1(models.Model): _inherit = 'extension.0' description = fields.Char(default="Extended")
并使用它们:
env = self.env {'name': "A", 'description': "Extended"}
提示:它还将产生各种自动字段,除非它们已被禁用。
第二个继承机制(委托)允许将模型的每个记录链接到父模型中的记录,并提供对父记录字段的透明访问。
委托(Delegation)
第三种继承机制提供了更大的灵活性(可以在运行时改变),但更少的功能:使用_inherits模型将当前模型中未发现的任何字段的查找委托给“children”模型。该委托是通过在父模型上自动建立的引用字段来执行的:
class Child0(models.Model): _name = 'delegation.child0' field_0 = fields.Integer() class Child1(models.Model): _name = 'delegation.child1' field_1 = fields.Integer() class Delegating(models.Model): _name = 'delegation.parent' _inherits = { 'delegation.child0': 'child0_id', 'delegation.child1': 'child1_id', } child0_id = fields.Many2one('delegation.child0', required=True, ondelete='cascade') child1_id = fields.Many2one('delegation.child1', required=True, ondelete='cascade')
record = env['delegation.parent'].create({ 'child0_id': env['delegation.child0'].create({'field_0': 0}).id, 'child1_id': env['delegation.child1'].create({'field_1': 1}).id, }) record.field_0 record.field_1
将会产生结果:
0
1
并且可以直接在委托字段上填写:
record.write({'field_1': 4})
警告!
当使用委托继承时,方法不是继承的,只能是字段。
二、视图继承
Odoo并没有修改现有的视图(通过重写它们),而是提供了视图继承,在这些视图中,子类的“扩展”视图应用于根视图之上,并且可以添加或删除来自其父视图的内容。
扩展视图使用inherit_id字段引用它的父视图,而不是单个视图,它的arch字段由任意数量的xpath元素组成,它们选择并更改其父视图的内容:
<!-- improved idea categories list --> <record id="idea_category_list2" model="ir.ui.view"> <field name="name">id.category.list2</field> <field name="model">idea.category</field> <field name="inherit_id" ref="id_category_list"/> <field name="arch" type="xml"> <!-- find field description and add the field idea_ids after it --> <xpath expr="//field[@name='description']" position="after"> <field name="idea_ids" string="Number of ideas"/> </xpath> </field> </record>
expr:
在父视图中选择单个元素的XPath表达式。如果匹配不到或匹配多于一个元素,则会引发错误。
position:
应用于匹配元素的操作
1)inside
在匹配元素的末尾添加xpath的主体。
2)replace
用xpath的主体替换匹配的元素,用原始元素替换新主体中的出现的任何$0节点。
3)before
在匹配的元素之前作为成员插入xpath的主体。
4)after
在匹配的元素之后作为成员插入xpath的主体。
5)attributes
在xpath的主体中使用特殊的属性元素改变匹配元素的属性。
当匹配单个元素时,可以直接在要找到的元素上设置position属性。以下两种继承都将给出相同的结果 <xpath expr="//field[@name='description']" position="after"> <field name="idea_ids" /> </xpath> <field name="description" position="after"> <field name="idea_ids" /> </field>
练习:
修改当前内容
- 使用模型继承,修改当前的Partner 模型以添加一个
instructor(讲师)布尔字段,以及一个课程时间表中合作伙伴的many2many字段。 - 使用视图继承,在partner form视图中显示此字段。
1、创建一个文件openacademy/models/partner.py,并且在__init__.py将文件用import导入
2、创建一个文件openacademy/views/partner.xml,并且加入到__manifest__.py
#openacademy/__init__.py # -*- coding: utf-8 -*- from . import controllers from . import models from . import partner
#openacademy/__manifest__.py # 'security/ir.model.access.csv', 'templates.xml', 'views/openacademy.xml', 'views/partner.xml', ], # only loaded in demonstration mode 'demo': [
#openacademy/partner.py # -*- coding: utf-8 -*- from odoo import fields, models class Partner(models.Model): _inherit = 'res.partner' # Add a new column to the res.partner model, by default partners are not # instructors instructor = fields.Boolean("Instructor", default=False) session_ids = fields.Many2many('openacademy.session', string="Attended Sessions", readonly=True)
######openacademy/views/partner.xml####注释##### <?xml version="1.0" encoding="UTF-8"?> <odoo> <!-- Add instructor field to existing view --> <record model="ir.ui.view" id="partner_instructor_form_view"> <field name="name">partner.instructor</field> <field name="model">res.partner</field> <field name="inherit_id" ref="base.view_partner_form"/> <field name="arch" type="xml"> <notebook position="inside"> <page string="Sessions"> <group> <field name="instructor"/> <field name="session_ids"/> </group> </page> </notebook> </field> </record> <record model="ir.actions.act_window" id="contact_list_action"> <field name="name">Contacts</field> <field name="res_model">res.partner</field> <field name="view_mode">tree,form</field> </record> <menuitem id="configuration_menu" name="Configuration" parent="main_openacademy_menu"/> <menuitem id="contact_menu" name="Contacts" parent="configuration_menu" action="contact_list_action"/> </odoo>
浙公网安备 33010602011771号