Mastering Computed Fields with Smart Triggers || @api.depends
How @api.depends work, what is this?
In the world of Odoo development, one of
the most elegant and powerful features is the computed field. These fields
don't store their values directly in the database (unless specified); instead,
their values are dynamically calculated based on other data. But how does Odoo
know when to perform these calculations? Enter the unsung hero: the
@api.depends decorator.
If you've ever constructed a form in Odoo where modifying one field automatically updates another (such as a total automatically recalculating when you alter a quantity), then you've seen @api.depends in use. It's the way your Odoo applications always have consistent and current derived data displayed.
So let's get started!
What is @api.depends?
Essentially, @api.depends(*fields) is a decorator you put right above a Python method of your Odoo model. This method will be assigned to be the "compute" function for a fields.Computed field
(or implicitly for fields.Related fields).
Its job is straightforward but deep: It tracks dependencies. You are literally saying to Odoo: "This specific computed field's value depends on these particular source fields. If any of those source fields are modified, please re-execute my computation method to obtain the new value."
Imagine it as if it were a spreadsheet formula. When you modify a number in cell A1, and cell C1 contains a formula such as =A1+B1, cell C1 will update automatically. In Odoo, @api.depends is the way you declare what "cells" (fields) are included in your "formula."
How Does it Work Under the Hood?
When a field is changed by a user either through the UI or programmatic, Odoo's ORM (Object-Relational Mapper) is always listening.
1.\tChange Detection: The ORM listens and notices that the value of a field has changed.
2.\tDependency Lookup: It looks into its internal registry and checks whether any computed fields have specified this changed field in their @api.depends decorator.
3. Queueing for Re-computation: When a dependency is detected, Odoo schedules the respective compute method for execution.
4. Calculation & Update: The compute
method is executed, computes the new value, and sets it on the computed field.
5. Persistence (Optional): If the
computed field is specified with store=True, the newly calculated value is saved back into the database too. If store=False (default), the value is computed on-the-fly when needed but not saved persistently.
Why @api.depends is Indispensable?
• Automatic Data Consistency:
• Automatic Data Consistency: It's the biggest benefit. Your computed data is always up-to-date with its source. No stale totals or outdated statuses anymore because of hand errors or omitted updates.
• Less Manual Logic: You don't have to implement complex onchange methods for each and every field which can influence a computation.
@api.depends takes care of change propagation automatically.
•\tCleaner Code: By isolating the computation logic from the field definitions, your
model code is more modular and easier to read. The dependencies are explicitly declared, so it is clear why a field is re-calculating.
•\tOptimized Performance: Odoo's ORM is intelligent. It queues re-computations only for fields whose dependencies have truly changed, not doing unnecessary calculations.
Real-World Examples in Action
Let us see how @api.depends gives dynamic power to your Odoo models.Example 1: Simple Calculation on the Same
Model
A
classic scenario: calculating a total_amount
from quantity and price_unit on a sales line.
Python
from odoo import models, fields, api
class SaleOrderLine(models.Model):
_name = 'sale.order.line'
_description
= 'Sales Order Line'
quantity = fields.Float(string="Quantity", default=1.0)
price_unit = fields.Float(string="Unit Price", default=0.0)
total_amount = fields.Float(
string="Total",
compute='_compute_total_amount',
store=True # Store in DB so it's searchable and reports are fast
)
@api.depends('quantity', 'price_unit') # If quantity OR price_unit
changes, recompute total_amount
def _compute_total_amount(self):
for
line in self:
line.total_amount = line.quantity *
line.price_unit
In
this example, as soon as quantity or price_unit is changed (and the record saved or an onchange triggered), total_amount will update.
Example 2: Aggregation on One2many Records
This
is incredibly common for summing up line items to get a grand total on a parent
document (like a Sale Order's total from its lines).
Python
from odoo import models, fields, api
class SaleOrder(models.Model):
_name = 'sale.order'
_description
= 'Sales Order'
#
... other fields for the main order ...
partner_id = fields.Many2one('res.partner', string="Customer")
#
One2many relation to the order lines
order_line_ids = fields.One2many('sale.order.line', 'order_id', string='Order
Lines')
#
Computed field for the grand total
amount_total = fields.Float(
string="Total Amount",
compute='_compute_amount_total',
store=True # Store the grand total for reporting/analysis
)
@api.depends('order_line_ids.total_amount') # <--- KEY SYNTAX HERE!
def _compute_amount_total(self):
"""
Computes the sum of total_amount from all order lines. """
for
order in self:
order.amount_total = sum(line.total_amount for line in
order.order_line_ids)
Notice
the syntax @api.depends('order_line_ids.total_amount'). When depending on a field
of a Many2many or One2many, you must specify one2many_field_name.field_on_child_model. Odoo will monitor changes
to the total_amount field on any line related to this SaleOrder record.
Example 3: Leveraging Related Fields
While
not directly a compute method, fields.Related is a special type of computed field. It simply pulls a
value from a linked record. It's implicitly stored if the source field is
stored.
Python
from odoo import models, fields
class SaleOrder(models.Model):
_name = 'sale.order'
_description = 'Sales Order'
partner_id = fields.Many2one('res.partner', string="Customer")
#
This field automatically gets the partner's phone number
partner_phone = fields.Char(
string="Customer Phone",
related='partner_id.phone', # Pulls 'phone' from the linked
'res.partner' record
readonly=True # Related fields are typically read-only
)
If partner_id.phone changes, partner_phone will automatically reflect the new value. If you had
another computed field that depended on partner_phone, you would list it in its @api.depends decorator.
The Pitfall: Overlooked Dependencies
The most frequent error when using computed fields is omitting all their dependencies from the @api.depends decorator.
What goes wrong?
Your computed field will simply not get recalculated when the unlisted source field changes. This causes stale data and annoying bugs that can be difficult to track down, since the field may seem to work at times but then fail at others.
Debugging Tip: When a computed field is not updating, the first thing to look at is its @api.depends decorator. Make sure all fields the compute method is calculating against (including fields on related models) are included in the list.
The @api.depends decorator is a tiny snippet of code that gives you incredible power and reliability in your Odoo modules. Using it, you can create dynamic, solid, and stable applications that instantly maintain data as fresh and accurate as possible.
Learn to use it, know its subtleties, and your Odoo development experience will be much smoother.
For odoo services please visit www.odoie.com
Post a Comment