Enable Dark Mode!
comparison-between-computed-and-onchange-in-odoo-19.jpg
By: Fathima Mazlin AM

Comparison Between Computed and Onchange in Odoo 19

Technical Odoo 19 Odoo Enterprises Odoo Community

Business models are the foundation of every Odoo module, and the way these models interact with each other plays a major role in how the application behaves. Each model is made up of multiple fields, which broadly fall into two categories: basic (scalar) fields and relational fields.Basic fields are used to store simple data such as text, numbers, dates, or boolean values. Relational fields, on the other hand, define how one model is connected to another, allowing Odoo to manage relationships like one-to-many or many-to-many across different business objects.

In real-world business scenarios, field values are often not independent. Sometimes the value of one field depends on another field, or even on multiple fields. In other cases, we may want a field to update automatically when a user changes a value on the form view. Odoo provides two powerful mechanisms to handle such requirements: Computed fields and Onchange methods.

In this blog, we will explore both approaches and clearly understand the differences between Compute fields and Onchange fields in Odoo, including when and why each one should be used.

Computed Fields

A computed field in Odoo is a field whose value is not entered manually by the user. Instead, its value is automatically calculated using a Python method. This type of field can be either a basic field or a relational field, and it is defined by adding the compute attribute in the field declaration.

To understand this better, consider a simple business model used to manage rental orders. In such a case, the total rental amount is usually derived from the rental duration and the price per unit of time. Since this value depends on other fields, it makes sense to calculate it automatically rather than asking the user to enter it.

For this requirement, we can define a monetary field called total_rent. This field does not store a fixed value entered by the user; instead, its value is dynamically calculated based on one or more related fields.

from odoo import models, fields
class VehicleRental(models.Model):
  _name = "vehicle.rental"
    _description = "Vehicle Rental"
    hour_rate = fields.Monetary(string="Hour Rate", required=True)
  hours = fields.Float(string="Hours", required=True, default=1.0)
    total_rent= fields.Monetary(string='Total Rent', compute='_compute_total_rent')
    currency_id = fields.Many2one('res.currency', string='Currency',
   default=lambda self: self.env.company.currency_id)
    def _compute_total_rent(self):
        for record in self:
            record.total_rent = record.hour_rate * record.hours

In the Vehicle Rental module, the total_rent field is defined as a computed field. Its value is calculated by multiplying the hourly rental charge with the total number of hours. Unlike regular fields, whose values are saved directly in the database, computed fields do not store their values permanently. Instead, their values are generated dynamically using the compute method defined in the model.

Whenever the dependent fields are updated, the compute method is automatically triggered to recalculate the value. It is important to note that each compute method must explicitly assign a value to the computed field for every record; failing to do so will result in a system error.

Depends

The *depends(args) function returns a decorator that defines which fields a compute method relies on. Each argument passed to the @depends() decorator must be a string representing a field name. It is also possible to pass a method instead of field names; in that case, the dependencies are determined by executing that method.

Since computed fields usually rely on the values of other fields, it is considered good practice to clearly declare those dependencies within the compute method. The Odoo ORM uses the @depends() decorator to understand when the compute method should be triggered. Whenever any of the specified dependent fields change, the ORM automatically recomputes the value of the computed field.

from odoo import api, models, fields
class VehicleRental(models.Model):
   _name = "vehicle.rental"
   _description = "Vehicle Rental"
   hour_rate = fields.Monetary(string="Hour Rate", required=True)
   hours = fields.Float(string="Hours", required=True, default=1.0)
   total_rent = fields.Monetary(string='Total Rent', compute='_compute_total_rent')
   currency_id = fields.Many2one('res.currency', string='Currency',
 
default=lambda self: self.env.company.currency_id) 
   @api.depends("hour_rate", "hour_rate")
   def _compute_total_rent(self):
       for record in self:
           record.total_rent = record.hour_rate * record.hours

Dependencies in the @api.depends() decorator can also be defined using a field path, such as @api.depends('move_id.state'). This allows the compute method to be triggered when a specific field of a related record is modified.

The relational field used in such dependency paths can be of any type, including Many2one, One2many, or Many2many. This approach works across all relational fields and helps ensure that computed values remain accurate when related data changes.

Below is an example where a function is used as the argument.

from odoo import api, models, fields
class VehicleRental(models.Model):
   _name = "vehicle.rental"
   _description = "Vehicle Rental"
 
   hour_rate = fields.Monetary(string="Hour Rate", required=True)
   hours = fields.Float(string="Hours", required=True, default=1.0)
   total_rent = fields.Monetary(string='Total Rent', compute='_compute_total_rent')
   
   currency_id = fields.Many2one('res.currency', string='Currency',
 default=lambda self: self.env.company.currency_id)
   def arguments(self):
       return ['hour_rate', 'hours']
   @api.depends(lambda self: self.arguments())
   def _compute_total_rent(self):
       for record in self:
           record.total_rent = record.hour_rate * record.hours

Now that we have a clear understanding of computed fields, let’s move on to the next topic and explore onchange fields in Odoo in the following section of this blog.

Onchange

The onchange(*args) method returns a decorator that is used to define an onchange method for specific fields. Each argument passed to onchange must be a single field name, and it can be either a scalar field or a relational field. However, if a dotted field name is used (for example, partner_id.name), it will be ignored by the decorator.

The onchange() method works on a recordset of pseudo-records, which means the data has not yet been saved to the database. Because of this, it is strongly recommended to avoid calling any CRUD operations such as create(), read(), write(), or unlink() inside an onchange method.

In Odoo, the onchange mechanism is mainly used to automatically update or adjust the value of a field when another field changes. Onchange methods are triggered only in the form view, and they execute when any of the specified fields are modified by the user.

For example, in a rental order form, the name can be generated through an onchange method. By using the @onchange() decorator, the method is triggered whenever the vehicle_id field changes, allowing the name field to be updated instantly based on the selected vehicle.

from odoo import api, models, fields
class VehicleRental(models.Model):
   _name = "vehicle.rental"
   _description = "Vehicle Rental"
   name = fields.Char(string="Name")
   vehicle_id = fields.Many2one('fleet.vehicle', string="Vehicle")
   @api.onchange('vehicle_id')
   def _onchange_vehicle(self):
       self.name = "Rental Order for %s" % self.vehicle_id.name

Onchange methods are not triggered automatically when records are created programmatically or through code. They are executed only during user interactions in the form view. It is also important to note that each argument passed to the @onchange decorator must refer to a single field.

Now that we have a clear understanding of both computed fields and onchange fields, let’s move on to the next section of the blog to explore the key differences between them.

Differences Between Compute and Onchange in Odoo

1. Method Visibility

Compute methods are considered private by convention and are usually meant to handle internal field calculations.

2. How and When Values Are Updated

A computed field does not store its value in the database by default. Instead, its value is calculated every time the field is accessed,for example, when opening a form view, a tree view, or when the field is accessed through code.

In contrast, the onchange mechanism allows the client interface to update field values dynamically in the form view without saving anything to the database. An onchange method is triggered only when one of the specified fields is modified by the user.

3. Field Value Assignment

Every compute method must always assign a value to the computed field, regardless of the condition, to avoid inconsistent behavior.

Onchange methods, however, are more flexible. They are not limited to assigning field values and can also be used to display warnings or non-blocking error messages.

4. Scope of Execution

Compute methods are triggered whenever the computed field is accessed, whether from views or programmatically. Since they work outside the form view context as well, computed fields are generally preferred for implementing business logic.

Onchange methods are not triggered when records are created or updated through code, which makes them unsuitable for core business logic.

5. Debugging and Maintainability

Computed fields are easier to debug because the logic responsible for setting the field value is directly linked in the field definition.

With onchange methods, debugging can be more challenging, as multiple onchange methods may modify the same field, making it harder to track where the value originates.

6. Recordset Handling

Since compute methods are executed when the field is accessed, they may receive multiple records in self. Therefore, it is important to iterate over self to avoid singleton errors.

Onchange methods, on the other hand, are triggered by user interaction in the form view and usually operate on a single record, so iteration is generally not required.

Additionally, compute dependencies can use dotted field names and can even depend on a single method.

7. Decorator Arguments and CRUD Operations

The arguments passed to the @onchange() decorator must be simple field names. If dotted names are used, they are ignored by the decorator.

Since onchange methods operate on pseudo-records, it is strongly recommended to avoid using CRUD operations (create, read, write, unlink) within them.

Computed fields and onchange methods address different needs within Odoo. Computed fields ensure data consistency by recalculating values whenever they are accessed, making them suitable for business logic and backend processing. Onchange methods are primarily intended for improving form behavior by reacting instantly to user input without affecting the database. Choosing the appropriate approach based on the use case leads to clearer code, easier debugging, and a more reliable application overall.

To read more about Overview of Onchange Methods in Odoo 19, refer to our blog Overview of Onchange Methods in Odoo 19.


Frequently Asked Questions

What is the main difference between a computed field and an onchange method?

A computed field calculates its value automatically based on other fields and can be accessed anywhere (views, reports, Python code).An onchange method only reacts to user input in the form view and updates values temporarily before the record is saved.

Are computed fields stored in the database?

By default, no. Computed fields are calculated dynamically when accessed.However, they can be stored by using store=True if persistence and performance optimization are required.

Can computed fields depend on relational fields?

Yes.Computed fields can depend on relational paths like: @api.depends('order_id.state') This works with Many2one, One2many, and Many2many fields.

What happens if a compute method does not assign a value?

Odoo raises an error.Every compute method must assign a value for every record in self, even if the value is False or 0.

If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message