Chapter 8: Computed Fields And Onchanges



The relations between models are a key component of any Odoo module. They are necessary for the modelization of any business case. However, we may want links between the fields within a given model. Sometimes the value of one field is determined from the values of other fields and other times we want to help the user with data entry.

These cases are supported by the concepts of computed fields and onchanges. Although this chapter is not technically complex, the semantics of both concepts is very important. This is also the first time we will write Python logic. Until now we haven’t written anything other than class definitions and field declarations.

Computed Fields 计算字段

Reference: the documentation related to this topic can be found in Computed Fields.


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

  • In the property model, the total area and the best offer should be computed:

Compute fields

  • In the property offer model, the validity date should be computed and can be updated:

Compute field with inverse

In our real estate module, we have defined the living area as well as the garden area. It is then natural to define the total area as the sum of both fields. We will use the concept of a computed field for this, i.e. the value of a given field will be computed from the value of other fields.

So far fields have been stored directly in and retrieved directly from the database. Fields can also be computed. In this case, the field’s value is not retrieved from the database but computed on-the-fly by calling a method of the model.

To create a computed field, create a field and set its attribute compute to the name of a method. The computation method should set the value of the computed field for every record in self.

By convention, compute methods are private, meaning that they cannot be called from the presentation tier, only from the business tier (see Chapter 1: Architecture Overview). Private methods have a name starting with an underscore _.

Dependencies 依赖性

The value of a computed field usually depends on the values of other fields in the computed record. The ORM expects the developer to specify those dependencies on the compute method with the decorator depends(). The given dependencies are used by the ORM to trigger the recomputation of the field whenever some of its dependencies have been modified:

from odoo import api, fields, models

class TestComputed(models.Model):
    _name = "test.computed"

    total = fields.Float(compute="_compute_total")
    amount = fields.Float()

    def _compute_total(self):
        for record in self:
   = 2.0 * record.amount


self is a collection. self是一个集合。

The object self is a recordset, i.e. an ordered collection of records. It supports the standard Python operations on collections, e.g. len(self) and iter(self), plus extra set operations such as recs1 | recs2.
对象self是一个记录集,即记录的有序集合。它支持集合的标准Python操作,例如len(self)iter(self),以及额外的集合操作,如recs1 | recs2

Iterating over self gives the records one by one, where each record is itself a collection of size 1. You can access/assign fields on single records by using the dot notation, e.g.

Many examples of computed fields can be found in Odoo. Here is a simple one.


Compute the total area. 计算总面积。

  • Add the total_area field to It is defined as the sum of the living_area and the garden_area.
  • Add the field in the form view as depicted on the first image of this section’s Goal.

For relational fields it’s possible to use paths through a field as a dependency:

description = fields.Char(compute="_compute_description")
partner_id = fields.Many2one("res.partner")

def _compute_description(self):
    for record in self:
        record.description = "Test for partner %s" %

The example is given with a Many2one, but it is valid for Many2many or a One2many. An example can be found here.

Let’s try it in our module with the following exercise!


Compute the best offer. 计算最佳报价。

  • Add the best_price field to It is defined as the highest (i.e. maximum) of the offers’ price.
  • Add the field to the form view as depicted in the first image of this section’s Goal.

Tip: you might want to try using the mapped() method. See here for a simple example.

Inverse Function 逆向函数

You might have noticed that computed fields are read-only by default. This is expected since the user is not supposed to set a value.

In some cases, it might be useful to still be able to set a value directly. In our real estate example, we can define a validity duration for an offer and set a validity date. We would like to be able to set either the duration or the date with one impacting the other.

To support this Odoo provides the ability to use an inverse function:

from odoo import api, fields, models

class TestComputed(models.Model):
    _name = "test.computed"

    total = fields.Float(compute="_compute_total", inverse="_inverse_total")
    amount = fields.Float()

    def _compute_total(self):
        for record in self:
   = 2.0 * record.amount

    def _inverse_total(self):
        for record in self:
            record.amount = / 2.0

An example can be found here. 示例可以在此处找到。

A compute method sets the field while an inverse method sets the field’s dependencies.

Note that the inverse method is called when saving the record, while the compute method is called at each change of its dependencies.


Compute a validity date for offers. 为报价计算有效日期。

  • Add the following fields to the model:

Where date_deadline is a computed field which is defined as the sum of two fields from the offer: the create_date and the validity. Define an appropriate inverse function so that the user can set either the date or the validity.

Tip: the create_date is only filled in when the record is created, therefore you will need a fallback to prevent crashing at time of creation.

  • Add the fields in the form view and the list view as depicted on the second image of this section’s Goal.

Additional Information 附加信息

Computed fields are not stored in the database by default. Therefore it is not possible to search on a computed field unless a search method is defined. This topic is beyond the scope of this training, so we won’t cover it. An example can be found here.

Another solution is to store the field with the store=True attribute. While this is usually convenient, pay attention to the potential computation load added to your model. Lets re-use our example:
另一种解决方案是在字段上使用 store=True 属性来存储该字段。虽然这通常很方便,但要注意给你的模型带来的潜在计算负载。让我们重新使用我们的示例:

description = fields.Char(compute="_compute_description", store=True)
partner_id = fields.Many2one("res.partner")

def _compute_description(self):
    for record in self:
        record.description = "Test for partner %s" %

Every time the partner name is changed, the description is automatically recomputed for all the records referring to it! This can quickly become prohibitive to recompute when millions of records need recomputation.

It is also worth noting that a computed field can depend on another computed field. The ORM is smart enough to correctly recompute all the dependencies in the right order… but sometimes at the cost of degraded performance.

In general performance must always be kept in mind when defining computed fields. The more complex is your field to compute (e.g. with a lot of dependencies or when a computed field depends on other computed fields), the more time it will take to compute. Always take some time to evaluate the cost of a computed field beforehand. Most of the time it is only when your code reaches a production server that you realize it slows down a whole process. Not cool 🙁
在定义计算字段时,始终要考虑性能。计算字段越复杂(例如,具有大量依赖项或计算字段依赖于其他计算字段),计算所需的时间就越长。务必提前花时间评估计算字段的成本。大多数时候,只有当你代码达到生产服务器时才会意识到它减慢了整个过程。不太酷 🙁


Reference: the documentation related to this topic can be found in onchange():
参考文献:与本主题相关的文档可在 onchange 中找到。


Goal: at the end of this section, enabling the garden will set a default area of 10 and an orientation to North.
目标:在这个部分结束时,启用花园将设置默认面积为 10 并将朝向设为北。Onchange

In our real estate module, we also want to help the user with data entry. When the ‘garden’ field is set, we want to give a default value for the garden area as well as the orientation. Additionally, when the ‘garden’ field is unset we want the garden area to reset to zero and the orientation to be removed. In this case, the value of a given field modifies the value of other fields.

The ‘onchange’ mechanism provides a way for the client interface to update a form without saving anything to the database whenever the user has filled in a field value. To achieve this, we define a method where self represents the record in the form view and decorate it with onchange() to specify which field it is triggered by. Any change you make on self will be reflected on the form:
“onchange”机制提供了这样一种方式:当用户填写了一个字段值时,客户端界面可以在不保存任何东西的情况下更新表单。为了实现这一点,我们定义了一个方法,其中 self 表示表单视图中的记录,并用 onchange() 装饰器指定它由哪个字段触发。你在 self 上做的任何改变都将反映在表单上:

from odoo import api, fields, models

class TestOnchange(models.Model):
    _name = "test.onchange"

    name = fields.Char(string="Name")
    description = fields.Char(string="Description")
    partner_id = fields.Many2one("res.partner", string="Partner")

    def _onchange_partner_id(self): = "Document for %s" % (
        self.description = "Default description for %s" % (

In this example, changing the partner will also change the name and the description values. It is up to the user whether or not to change the name and description values afterwards. Also note that we do not loop on self, this is because the method is only triggered in a form view, where self is always a single record.
在这个例子中,更改合作伙伴也会更改名字和描述的值。之后是否要更改名字和描述的值取决于用户。还要注意,我们不需要循环遍历 self,这是因为该方法只在表单视图中触发,self 总是一条记录。


Set values for garden area and orientation. 设置花园面积和方向的值。

Create an onchange in the model in order to set values for the garden area (10) and orientation (North) when garden is set to True. When unset, clear the fields. 模型中创建一个 onchange,以便在设置花园为 True 时设置花园面积(10)和方向(北)。取消设置时,清除这些字段。

Additional Information 额外信息

Onchanges methods can also return a non-blocking warning message (example).
onchange 方法还可以返回非阻塞警告消息(示例)

How to use them? 如何使用它们?

There is no strict rule for the use of computed fields and onchanges.
对于何时使用计算字段和 onchange 没有严格的规定。

In many cases, both computed fields and onchanges may be used to achieve the same result. Always prefer computed fields since they are also triggered outside of the context of a form view. Never ever use an onchange to add business logic to your model. This is a very bad idea since onchanges are not automatically triggered when creating a record programmatically; they are only triggered in the form view.
在许多情况下,计算字段和 onchange 可能用于实现相同的结果。总是优先选择计算字段,因为它们也在表单视图之外触发。永远不要使用 onchange 在您的模型中添加业务逻辑。这是一个非常糟糕的想法,因为 onchange 不会在程序化创建记录时自动触发;它们只在表单视图中触发。

The usual pitfall of computed fields and onchanges is trying to be ‘too smart’ by adding too much logic. This can have the opposite result of what was expected: the end user is confused from all the automation.
计算字段和 onchange 的常见陷阱是试图通过添加过多的逻辑来显得“太聪明”。这可能导致相反的结果:最终用户对自动化感到困惑。

Computed fields tend to be easier to debug: such a field is set by a given method, so it’s easy to track when the value is set. Onchanges, on the other hand, may be confusing: it is very difficult to know the extent of an onchange. Since several onchange methods may set the same fields, it easily becomes difficult to track where a value is coming from.
计算字段往往更容易调试:这样的字段是由特定的方法设置的,所以很容易跟踪何时设置值。另一方面,onchange 可能令人困惑:很难知道 onchange 的作用范围。由于多个 onchange 方法可以设置相同的字段,因此很难追踪值来自何处。

When using stored computed fields, pay close attention to the dependencies. When computed fields depend on other computed fields, changing a value can trigger a large number of recomputations. This leads to poor performance.

In the next chapter, we’ll see how we can trigger some business logic when buttons are clicked.



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