第12章:继承¶
Odoo 的一个强大的方面是它的模块化。模块专用于业务需求,但模块之间也可以交互。这对于扩展现有模块的功能很有用。例如,在我们的房地产场景中,我们希望直接在常规用户视图中显示销售人员的房产列表。
但在讨论特定的 Odoo 模块继承之前,让我们看看如何改变标准 CRUD(创建、检索、更新或删除)方法的行为。
Python继承¶
注解
目标:在本节末尾:
不应该删除非新建或已取消的属性。
创建报价后,属性状态应更改为“已收到报价”
不可能创建比现有报价更低的报价
在我们的房地产模块中,我们从来不需要开发任何特定的东西来能够执行标准的 CRUD 操作。 Odoo 框架提供了完成这些任务所需的工具。事实上,由于经典的 Python 继承,此类操作已经包含在我们的模型中:
from odoo import fields, models
class TestModel(models.Model):
_name = "test_model"
_description = "Test Model"
...
我们的``class TestModel``继承自:class:~odoo.models.Model,它提供:meth:~odoo.models.Model.create、read()、unlink()。
这些方法(以及 Model 上定义的任何其他方法)可以扩展以添加特定的业务逻辑:
from odoo import fields, models
class TestModel(models.Model):
_name = "test_model"
_description = "Test Model"
...
@api.model
def create(self, vals_list):
# Do some business logic, modify vals...
...
# Then call super to execute the parent method
return super().create(vals_list)
装饰器 model() 对于 create() 方法是必需的,因为记录集 self 的内容与创建上下文无关,但对于其他 CRUD 方法则不是必需的。
同样重要的是要注意,即使我们可以直接重写 unlink() 方法,您几乎总是想用装饰器 ondelete() 编写一个新方法。标有此装饰器的方法将在 unlink() 期间被调用,并避免在直接覆盖 unlink() 时卸载模型模块时可能出现的一些问题。
在 Python 3 中,“super()` is equivalent to ``super(TestModel, self)`”。当您需要使用修改后的记录集调用父方法时,可能需要后者。
危险
Exercise
将业务逻辑添加到 CRUD 方法。
如果属性的状态不是“新建”或“已取消”,则防止删除该属性
提示:使用 ondelete() 装饰器创建一个新方法,并记住“self”可以是包含多个记录的记录集。
创建优惠时,将属性状态设置为“已收到优惠”。如果用户尝试创建金额低于现有报价的报价,也会引发错误。
提示: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) (`示例<https://github.com/odoo/odoo/blob/136e4f66cd5cafe7df450514937c7218c7216c93/addons/gamification/models/badge.py#L57>`__)
模型继承¶
参考:与此主题相关的文档可以在 继承与延伸 中找到。
在我们的房地产模块中,我们希望在“设置/用户和公司/用户”表单视图中直接显示链接到销售人员的属性列表。为此,我们需要向“res.users”模型添加一个字段并调整其视图以显示它。
Odoo 提供了两种“继承”机制来以模块化方式扩展现有模型。
第一个继承机制允许模块通过以下方式修改另一个模块中定义的模型的行为:
将字段添加到模型中,
覆盖模型中字段的定义,
向模型添加约束,
向模型添加方法,
覆盖模型中的现有方法。
第二个继承机制(委托)允许模型的每个记录链接到父模型的记录,并提供对此父记录的字段的透明访问。
在 Odoo 中,第一种机制是迄今为止使用最多的。在我们的例子中,我们想要向现有模型添加一个字段,这意味着我们将使用第一种机制。例如::
from odoo import fields, models
class InheritedModel(models.Model):
_inherit = ["inherited.model"]
new_field = fields.Char(string="New Field")
可以在 here 中找到将两个字段添加到模型中的实际示例。
按照惯例,每个继承的模型都在其自己的 Python 文件中定义。在我们的示例中,它将是“models/inherited_model.py”。
Exercise
向用户添加一个字段。
将以下字段添加到“
res.users”:
场地 |
类型 |
|---|---|
属性ID |
One2many 引用“ |
将域添加到该字段,以便它仅列出可用的属性。
在下一部分中,我们将字段添加到视图中并检查一切是否正常!
查看继承¶
参考:与此主题相关的文档可以在 遗产 中找到。
注解
目标:在本节末尾,链接到销售人员的可用属性列表应显示在其用户表单视图中
Odoo 不是直接修改现有视图(通过覆盖它们),而是提供视图继承,其中子“扩展”视图应用于根视图之上。这些扩展程序可以在其父视图中添加和删除内容。
扩展视图使用``inherit_id`` field. Instead of a single view, its arch field contains a number of ``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应用于匹配元素的操作:
匹配单个元素时,可以直接在要查找的元素上设置``position``属性。下面的两个继承具有相同的结果。
<xpath expr="//field[@name='description']" position="after">
<field name="idea_ids" />
</xpath>
<field name="description" position="after">
<field name="idea_ids" />
</field>
可以在 here 中找到视图继承扩展的示例。
Exercise
将字段添加到用户视图。
在新笔记本页面中添加“property_ids` field to the ``base.view_users_form`”。
提示:可以在 here 中找到用户视图继承的示例。
由于其模块化概念,继承在 Odoo 中被广泛使用。请立即阅读相应的文档以获取更多信息!
在:doc:`next chapter <13_other_module>`中,我们将学习如何与其他模块交互。