AI翻译及测试Odoo17开发教程,第十二章:继承

Chapter 12: Inheritance
第十二章:继承

A powerful aspect of Odoo is its modularity. A module is dedicated to a business need, but modules can also interact with one another. This is useful for extending the functionality of an existing module. For example, in our real estate scenario we want to display the list of a salesperson’s properties directly in the regular user view.
Odoo的一个强大之处在于其模块化。一个模块致力于满足一个业务需求,但模块之间也可以相互交互。这对于扩展现有模块的功能非常有用。例如,在我们的房地产场景中,我们希望在普通用户视图中直接显示销售员的房产列表。

But before going through the specific Odoo module inheritance, let’s see how we can alter the behavior of the standard CRUD (Create, Retrieve, Update or Delete) methods.
但在深入了解Odoo模块继承之前,让我们看看如何改变标准的CRUD(创建、检索、更新或删除)方法的行为。

Python Inheritance Python 继承

 注解

Goal: at the end of this section: 目标:在本节结束时:

  • It should not be possible to delete a property which is not new or canceled.
    不应该允许删除状态不是“新建”或“已取消”的房产。

Unlink

  • When an offer is created, the property state should change to ‘Offer Received’
    当创建报价时,房产状态应该更改为“收到报价”。
  • It should not be possible to create an offer with a lower price than an existing offer
    不应该允许创建比现有报价更低价格的报价。

Create

In our real estate module, we never had to develop anything specific to be able to do the standard CRUD actions. The Odoo framework provides the necessary tools to do them. In fact, such actions are already included in our model thanks to classical Python inheritance:
在我们的房地产模块中,我们从未需要开发任何特定的东西来执行标准的CRUD操作。Odoo框架提供了执行这些操作所需的工具。实际上,由于经典的Python继承,这些操作已经包含在我们的模型中:

from odoo import fields, models

class TestModel(models.Model):
    _name = "test_model"
    _description = "Test Model"

    ...

Our class TestModel inherits from Model which provides create()read()write() and unlink().
我们的TestModel类继承自Model,它提供了create()、read()、write()和unlink()方法。

These methods (and any other method defined on Model) can be extended to add specific business logic:
这些方法(以及在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)

The decorator model() is necessary for the create() method because the content of the recordset self is not relevant in the context of creation, but it is not necessary for the other CRUD methods.
model()装饰器对create()方法是必要的,因为在创建的上下文中记录集self的内容并不相关,但它对其他CRUD方法并不必要。

It is also important to note that even though we can directly override the unlink() method, you will almost always want to write a new method with the decorator ondelete() instead. Methods marked with this decorator will be called during unlink() and avoids some issues that can occur during uninstalling the model’s module when unlink() is directly overridden.
同样重要的是要注意,尽管我们可以直接覆盖unlink()方法,但几乎总是希望使用ondelete()装饰器编写一个新方法。用此装饰器标记的方法将在unlink()期间被调用,避免了在直接覆盖unlink()时在卸载模型模块期间可能发生的一些问题。

In Python 3, super() is equivalent to super(TestModel, self). The latter may be necessary when you need to call the parent method with a modified recordset.
在Python 3中,super()等同于super(TestModel, self)。当你需要用修改后的记录集调用父方法时,可能需要使用后者。

 危险

  • It is very important to always call super() to avoid breaking the flow. There are only a few very specific cases where you don’t want to call it.
    非常重要的是,总是要调用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 a dict().
    确保始终返回与父方法一致的数据。例如,如果父方法返回一个dict(),你的覆盖也必须返回一个dict()。

 Exercise

Add business logic to the CRUD methods. 将业务逻辑添加到CRUD方法中。

  • Prevent deletion of a property if its state is not ‘New’ or ‘Canceled’
    如果房产状态不是“新建”或“已取消”,则防止删除房产。

Tip: create a new method with the ondelete() decorator and remember that self can be a recordset with more than one record.
提示:使用ondelete()装饰器创建一个新方法,并记住self可能是一个包含多条记录的记录集。

  • At offer creation, set the property state to ‘Offer Received’. Also raise an error if the user tries to create an offer with a lower amount than an existing offer.
    在创建报价时,将房产状态设置为“收到报价”。如果用户尝试创建的报价金额低于现有报价,也引发一个错误。

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)
提示:vals中可用property_id字段,但它是一个int。要实例化一个estate.property对象,使用self.env[model_name].browse(value)(示例)

Model Inheritance 模型继承

Reference: the documentation related to this topic can be found in Inheritance and extension.
参考:与本主题相关的文档可以在继承和扩展中找到。

In our real estate module, we would like to display the list of properties linked to a salesperson directly in the Settings / Users & Companies / Users form view. To do this, we need to add a field to the res.users model and adapt its view to show it.
在我们的房地产模块中,我们希望在设置/用户和公司/用户表单视图中直接显示与销售员关联的房产列表。为此,我们需要向res.users模型添加一个字段,并调整其视图以显示它。

Odoo provides two inheritance mechanisms to extend an existing model in a modular way.
Odoo提供了两种继承机制,以模块化的方式扩展现有模型。

The first inheritance mechanism allows modules to modify the behavior of a model defined in an another module by:
第一种继承机制允许模块通过以下方式修改在另一个模块中定义的模型的行为:

  • adding fields to the model, 向模型中添加字段,
  • overriding the definition of fields in the model, 覆盖模型中字段的定义
  • adding constraints to the model, 向模型中添加约束,
  • adding methods to the model, 向模型中添加方法,
  • overriding existing methods in the model. 覆盖模型中的现有方法。

The second inheritance mechanism (delegation) allows every record of a model to be linked to a parent model’s record and provides transparent access to the fields of this parent record.
第二种继承机制(委托)允许每个模型记录与父模型的记录相关联,并提供对父记录字段的透明访问。Inheritance Methods

In Odoo, the first mechanism is by far the most used. In our case, we want to add a field to an existing model, which means we will use the first mechanism. For example:
在Odoo中,第一种机制迄今为止使用最为广泛。在我们的例子中,我们想要向现有模型添加一个字段,这意味着我们将使用第一种机制。例如:

from odoo import fields, models

class InheritedModel(models.Model):
    _inherit = "inherited.model"

    new_field = fields.Char(string="New Field")

A practical example where two fields are added to a model can be found here.
可以在此处找到一个实际的例子,其中向模型添加了两个字段。

By convention, each inherited model is defined in its own Python file. In our example, it would be models/inherited_model.py.
按照惯例,每个继承模型都在其自己的Python文件中定义。在我们的例子中,它将是models/inherited_model.py。

 Exercise

Add a field to Users. 向用户添加字段。

  • Add the following field to res.users: 向res.users添加以下字段:
FieldType
property_idsOne2many inverse of the field that references the salesperson in estate.property
One2many,与房产中引用销售员的字段的反向关联
  • Add a domain to the field so it only lists the available properties.
    为字段添加一个域,以便它只列出可用的房产。

In the next section let’s add the field to the view and check that everything is working well!
在下一节中,让我们将字段添加到视图中,并检查一切是否正常工作!

View Inheritance 视图继承

Reference: the documentation related to this topic can be found in Inheritance.
参考:与本主题相关的文档可以在继承中找到。

 注解

Goal: at the end of this section, the list of available properties linked to a salesperson should be displayed in their user form view
目标:在本节结束时,应将与销售员关联的可用房产列表显示在用户表单视图中。Users

Instead of modifying existing views in place (by overwriting them), Odoo provides view inheritance where children ‘extension’ views are applied on top of root views. These extension can both add and remove content from their parent view.
Odoo不提供在原地修改现有视图(通过覆盖它们),而是提供视图继承,其中子“扩展”视图应用于根视图之上。这些扩展可以从父视图中添加和移除内容。

An extension view references its parent using the inherit_id field. Instead of a single view, its arch field contains a number of xpath elements that select and alter the content of their parent view:
扩展视图使用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

An XPath expression selecting a single element in the parent view. Raises an error if it matches no element or more than one
一个XPath表达式,选择父视图中的单个元素。如果没有匹配到元素或匹配到多个元素,将引发错误。

position

Operation to apply to the matched element:
应用于匹配元素的操作:

inside

appends xpath’s body to the end of the matched element
将xpath主体追加到匹配元素的末尾

replace

replaces the matched element with the xpath’s body, replacing any $0 node occurrence in the new body with the original element
用xpath的主体替换匹配元素,将新主体中的任何$0节点出现替换为原始元素

before

inserts the xpath’s body as a sibling before the matched element
将xpath的主体作为匹配元素之前的兄弟节点插入

after

inserts the xpaths’s body as a sibling after the matched element
将xpath的主体作为匹配元素之后的兄弟节点插入

attributes

alters the attributes of the matched element using the special attribute elements in the xpath’s body
使用xpath主体中的特殊属性元素修改匹配元素的属性

When matching a single element, the position attribute can be set directly on the element to be found. Both inheritances below have the same result.
当匹配单个元素时,可以直接在要查找的元素上设置position属性。下面的两种继承具有相同的结果。

<xpath expr="//field[@name='description']" position="after">
    <field name="idea_ids" />
</xpath>

<field name="description" position="after">
    <field name="idea_ids" />
</field>

An example of a view inheritance extension can be found here.
可以在此处找到一个视图继承扩展的例子。

 Exercise

Add fields to the Users view. 向用户视图添加字段。

Add the property_ids field to the base.view_users_form in a new notebook page.
将property_ids字段添加到base.view_users_form的新笔记本页面中。

Tip: an example an inheritance of the users’ view can be found here.
提示:可以在此处找到一个用户视图继承的例子。

Inheritance is extensively used in Odoo due to its modular concept. Do not hesitate to read the corresponding documentation for more info!
由于其模块化概念,继承在Odoo中被广泛使用。不要犹豫阅读相应的文档以获取更多信息!

In the next chapter, we will learn how to interact with other modules.
在下一章中,我们将学习如何与其他模块交互。


评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注