Chapter 12: Inheritance¶
Odoo 的一个强大之处在于其模块化。每个模块都专注于一个业务需求,但模块之间也可以相互交互。这对于扩展现有模块的功能非常有用。例如,在我们的房地产场景中,我们希望直接在常规用户视图中显示销售人员的房产清单。
在深入了解特定的Odoo模块继承之前,让我们先看看如何修改标准的CRUD(创建、检索、更新或删除)方法的行为。
Python继承¶
注解
目标:在本节结束时:
It should not be possible to delete a property which is not new or cancelled.
当一个报价被创建时,物业状态应该变为“已收到报价”
不应该创建一个价格比现有报价更低的报价
在我们的房地产模块中,我们从未需要开发任何特定的内容来执行标准的CRUD操作。Odoo框架提供了必要的工具来执行它们。实际上,这些操作已经包含在我们的模型中,这要归功于经典的Python继承:
from odoo import fields, models
class TestModel(models.Model):
_name = "test_model"
_description = "Test Model"
...
我们的 class TestModel
继承自 Model
,它提供了 create()
、 read()
、 write()
和 unlink()
。
这些方法(以及在 Model
上定义的任何其他方法)可以扩展以添加特定的业务逻辑:
from odoo import fields, models
class TestModel(models.Model):
_name = "test_model"
_description = "Test Model"
...
@api.model
def create(self, vals):
# Do some business logic, modify vals...
...
# Then call super to execute the parent method
return super().create(vals)
装饰器 model()
对于 create()
方法是必需的,因为记录集 self
的内容在创建的上下文中不相关,但对于其他 CRUD 方法则不是必需的。
同时需要注意的是,即使我们可以直接覆盖 unlink()
方法,你几乎总是希望 使用装饰器 ondelete()
来编写一个新的方法。 使用这个装饰器标记的方法将在 unlink()
方法调用时被调用,避免了一些在 直接覆盖 unlink()
时可能发生的问题。
在Python 3中, super()
等同于 super(TestModel, self)
。当你需要使用修改后的记录集调用父方法时,可能需要使用后者。
危险
非常重要的是 始终 调用
super()
以避免中断流程。只有极少数非常特殊的情况下,您不想调用它。Make sure to always return data consistent with the parent method. For example, if the parent method returns a
dict()
, your override must also return adict()
.
Exercise
在CRUD方法中添加业务逻辑。
Prevent deletion of a property if its state is not ‘New’ or ‘Cancelled’
提示:使用 ondelete()
装饰器创建一个新的方法,并记住 self
可以是一个包含多个记录的记录集。
在创建报价时,将属性状态设置为“已收到报价”。如果用户尝试创建低于现有报价的报价,则引发错误。
Tip: the property_id
field is available in the vals
, but it is an int
. To
instantiate an estate.property
object, use self.env[model_name].browse(value)
(example)
模型继承¶
参考: 有关此主题的文档可以在 继承和扩展 中找到。
在我们的房地产模块中,我们希望在“设置/用户和公司/用户”表单视图中直接显示与销售员相关联的属性列表。为此,我们需要向 res.users
模型添加一个字段,并调整其视图以显示它。
Odoo提供了两种 继承 机制,以模块化的方式扩展现有模型。
第一种继承机制允许模块通过以下方式修改另一个模块中定义的模型的行为:
向模型添加字段,
覆盖模型中字段的定义,
向模型添加约束条件,
向模型添加方法,
在模型中覆盖现有方法。
第二种继承机制(委托)允许将模型的每个记录链接到父模型的记录,并提供对该父记录字段的透明访问。
在Odoo中,第一种机制是最常用的。在我们的情况下,我们想要向现有模型添加一个字段,这意味着我们将使用第一种机制。例如:
from odoo import fields, models
class InheritedModel(models.Model):
_inherit = "inherited.model"
new_field = fields.Char(string="New Field")
一个实际的例子,其中向模型添加了两个字段,可以在 这里 找到。
按照惯例,每个继承的模型都在自己的Python文件中定义。在我们的例子中,它将是 models/inherited_model.py
。
Exercise
向用户添加一个字段。
将以下字段添加到
res.users
:
字段 |
类型 |
---|---|
属性 ID |
One2many 反向字段,引用了 |
在字段中添加域,以便仅列出可用的属性。
在下一节中,让我们将该字段添加到视图中并检查一切是否正常!
视图继承¶
Reference: the documentation related to this topic can be found in 继承.
注解
目标:在本节结束时,应该在销售人员的用户表单视图中显示与其关联的可用属性列表
Odoo提供视图继承,而不是直接修改现有视图(通过覆盖它们)。子视图可以在根视图之上应用’扩展’视图。这些扩展可以添加和删除其父视图的内容。
扩展视图使用 inherit_id
字段引用其父视图。其 arch
字段不是单个视图,而是包含多个 xpath
元素,用于选择和修改其父视图的内容:
<record id="inherited_model_view_form" model="ir.ui.view">
<field name="name">inherited.model.form.inherit.test</field>
<field name="model">inherited.model</field>
<field name="inherit_id" ref="inherited.inherited_model_view_form"/>
<field name="arch" type="xml">
<!-- find field description and add the field
new_field after it -->
<xpath expr="//field[@name='description']" position="after">
<field name="new_field"/>
</xpath>
</field>
</record>
expr
一个XPath_表达式,在父视图中选择一个单独的元素。如果匹配不到元素或匹配到多个元素,则会引发错误。
position
要应用于匹配元素的操作:
inside
将
xpath
的主体附加到匹配元素的末尾replace
使用
xpath
的主体替换匹配的元素,在新主体中用原始元素替换任何$0
节点出现before
在匹配的元素之前插入
xpath
的内容作为兄弟元素after
在匹配的元素之后,将
xpaths
的内容作为兄弟元素插入attributes
使用
xpath
的主体中的特殊attribute
元素来修改匹配元素的属性
当匹配单个元素时,可以直接在要查找的元素上设置 position
属性。下面的两个继承都具有相同的结果。
<xpath expr="//field[@name='description']" position="after">
<field name="idea_ids" />
</xpath>
<field name="description" position="after">
<field name="idea_ids" />
</field>
一个视图继承扩展的示例可以在 这里 找到。
Exercise
向用户视图添加字段。
在一个新的笔记本页面中,将 property_ids
字段添加到 base.view_users_form
中。
提示:一个用户视图的继承示例可以在 这里 找到。
由于其模块化概念,继承在Odoo中被广泛使用。请不要犹豫,阅读相应的文档以获取更多信息!
In the next chapter, we will learn how to interact with other modules.