How to Use Onchange and Compute in Odoo

In odoo there are 2 ways of how to calculate the value of a field automatically. We can use the onchange decorator or the compute field parameter.

Use the onchange decorator if we want the value of some field change automatically when the value of another field is changed by the user. For example, let’s assume we have 3 fields like this.

# -*- coding: utf-8 -*-

from odoo import api, fields, models, _

class MyModel(models.Model):
    _name = 'my.model'

    field_1 = fields.Integer(string='Field 1')
    field_2 = fields.Integer(string='Field 2')
    result = fields.Integer(string='Result')

If we want the value of the result field to change automatically if the value of field_1 or field_2 is changed by the user, we can use the onchange decorator like below.

@api.onchange('field_1', 'field_2')
def calculate_result(self):
    self.result = self.field_1 + self.field_2

Insert array of fields that you want to be used as trigger in the onchange decorator as parameter. The number of fields that you can insert as trigger is not limited. Then followed by a method with a free name. While the program is running, if value one of the fields that we used as the trigger is changed by the user, the method, in this example the calculate_result method will be executed automatically.

This is an example of how a value has been changed automatically with the help of onchange decorator.

Field that we calculated with the onchange decorator still can be changed by the user manually, either from the frontend or from the backend (for example via SQL commands) and its value will be stored by odoo as long as the value of the trigger field does not change. This is an example of how a field have been calculated using the onchange decorator but then it is changed again by the user manually and then saved.

As mentioned earlier, field that we calculated with the onchange decorator still can be changed by the user manually, so we may need to add the readonly attribute to protect it. However, field that we marked with the readonly attribute, its value will not be stored in the database if we click the Save button. If you want its value to be stored, make sure to use the force_save attribute as shown below.

<field name="result" readonly="1" force_save="1" />

Method that we marked with the onchange decorator are only executed if the value of trigger field is changed from the frontend, or when the form is edited. So if the value of field_1 is changed from another model with python code or via SQL command the value of the result field will not change.

Furthermore, we can also use the compute field parameter to calculate the value of a field automatically. As show in the code below.

# -*- coding: utf-8 -*-

from odoo import api, fields, models, _

class MyModel(models.Model):
    _name = 'my.model'

    field_1 = fields.Integer(string='Field 1')
    field_2 = fields.Integer(string='Field 2')
    result = fields.Integer(string='Result', compute='compute_result')
    
    def compute_result(self):
        for rec in self:
            rec.result = rec.field_1 + rec.field_2

Unlike the onchange decorator, when we use the compute field parameter, make sure to loop the self parameter. Because if not, there will be an expected singleton error if there are a lot of data in the model. So avoid using code like below.

result = fields.Integer(string='Result', compute='compute_result')
    
def compute_result(self):
    self.result = self.field_1 + self.field_2

Field that we marked with the compute field parameter will automatically to be a readonly field. So that there is no possibility for the user to change the field value from the frontend.

Field that we marked with the compute field parameter, its value will not change until the form is saved. While still in create or edit mode, the field value will not change because the compute method has not been executed.

The compute method is only executed on read action. It is when we open the tree view or form view, but not in create or edit mode. After we click the Save button, and we exit the create or edit mode, the value will change automatically, as shown below.

The compute field parameter also give an advantage, if we change one of the fields that are used as the basis for calculation from the backend, with python code from any model or from the database via SQL command, the value will always change if we open the form next time.

From the two images above, it can be seen that the value of result changes even though the value of field_1 is changed from the database via SQL commands. This is not possible if we use the onchange decorator.

But if we want that the value of the result field to change immediately without having to click the save button, you can also use the depends decorator as below.

# -*- coding: utf-8 -*-

from odoo import api, fields, models, _

class MyModel(models.Model):
    _name = 'my.model'

    field_1 = fields.Integer(string='Field 1')
    field_2 = fields.Integer(string='Field 2')
    result = fields.Integer(string='Result', compute='compute_result')
    
    @api.depends('field_1', 'field_2')
    def compute_result(self):
        for rec in self:
            rec.result = rec.field_1 + rec.field_2

The way of how to write the depends decorator is the same as the onchange decorator. What make the depends decorator different from the onchange decorator is, if the value of the trigger field is changed from the backend with python code from any model or with an SQL command the value of the result field will change.

Field that we marked with the compute field parameter, by default, will not be translated as a column in the database table. So it is not possible for us to select this field in a SQL command.

From the picture above, you can see that there is no column named result. If we want this field to be translated into a column in the database table, we must add the store field parameter with the value of True, as shown below.

result = fields.Integer(string='Result', compute='compute_result', store=True)

Now the result field has been translated into a column in database table. Unfortunately, now if we make changes to the trigger field (field_1 or field_2) via SQL command, the value of result field does not change, the compute method is not executed. It is the same as when we used the onchange decorator.

But if we change the value of the trigger field with python code from any model, the value of the result field will change automatically, the compute method is still executed. Unlike when we use the onchange decorator.

This is the end of tutorial on how to use the onchange decorator and the compute field parameter to calculate the value of a field automatically. Use it as needed.

Related Article

Leave a Reply