In Odoo, dependencies with @api.depends are decorators that define computed fields and indicate which fields they rely on. You can instruct Odoo to automatically recalculate a computed field whenever any of its dependent fields change by using @api.depends. This removes the need for manual field updates and guarantees data consistency.
Understanding Computed Fields in Odoo
Instead of being directly stored in the database, computed fields have their values automatically determined based on other fields. To create these computed fields effectively, the @api.depends decorator is necessary.
amount_untaxed = fields.Float(string='Untaxed Amount')
amount_tax = fields.Float(string='Tax Amount')
amount_total = fields.Float(string='Total Amount', compute='_compute_amount_total')
@api.depends('amount_untaxed', 'amount_tax')
def _compute_amount_total(self):
for record in self:
record.amount_total = record.amount_untaxed + record.amount_tax
In this example, amount_total will automatically recalculate whenever amount_untaxed or amount_tax changes.
How @api.depends Works
When any of the designated fields are changed, the @api.depends decorator notifies the compute method. This system guarantees that calculated values are always correct and current.
Important traits:
Field names are passed as string arguments to the decorator. Multiple fields can be specified, with commas used to separate them. Odoo takes care of the recalculation automatically. Performance is enhanced by only calling the compute method when required.
Working with Related Fields
You can use dot notation to depend on fields from related models. This is particularly useful when you need to compute values based on data from linked records.
order_id = fields.Many2one('sale.order', string='Order')
product_id = fields.Many2one('product.product', string='Product')
quantity = fields.Float(string='Quantity')
price_unit = fields.Float(string='Unit Price', related='product_id.list_price')
subtotal = fields.Float(string='Subtotal', compute='_compute_subtotal')
@api.depends('quantity', 'product_id.list_price')
def _compute_subtotal(self):
for line in self:
line.subtotal = line.quantity * line.product_id.list_priceHere, the subtotal depends on both the line's quantity and the product's list price from the related product model.
Multiple Level Dependencies
You can chain dependencies across multiple related models using dot notation to traverse relationships.
@api.depends('order_line_ids.product_id.categ_id.name')
def _compute_category_summary(self):
for order in self:
categories = order.order_line_ids.mapped('product_id.categ_id.name')
order.category_summary = ', '.join(set(categories))Depending on One2many and Many2many Fields
When depending on One2many or Many2many fields, you typically need to specify which fields within those related records trigger the recalculation.
order_line = fields.One2many('purchase.order.line', 'order_id', string='Order Lines')
total_quantity = fields.Float(string='Total Quantity', compute='_compute_total_quantity')
@api.depends('order_line.product_qty')
def _compute_total_quantity(self):
for order in self:
order.total_quantity = sum(order.order_line.mapped('product_qty'))Stored vs Non-Stored Computed Fields
Computed fields can be stored in the database or calculated on-the-fly. Adding store=True to a computed field definition saves the calculated value to the database.
amount_total = fields.Float(
string='Total Amount',
compute='_compute_amount_total',
store=True
)
Benefits of storing computed fields:
- Because values are pre-calculated, retrieval is faster.
- is applicable to filters and database queries.
- Sortable and searchable in list views
- automatically updated in response to changes in dependencies
Common Failures and Effective Solutions
Always specify all dependencies. If you forget to include a field in @api.depends, the computed field won't update when that field changes, leading to incorrect data.
Use self iteration. Always iterate through records using for record in self within compute methods to handle recordsets properly.
Avoid circular dependencies. Don't create situations where Field A depends on Field B, and Field B depends on Field A.
Performance considerations. Be cautious with dependencies on large One2many fields or deeply nested related fields, as they can impact performance.
Practical Example: Invoice Computation
Here's a comprehensive example showing how @api.depends works in a real-world invoice scenario:
invoice_line_ids = fields.One2many('account.invoice.line', 'invoice_id', string='Invoice Lines')
amount_untaxed = fields.Float(string='Untaxed Amount', compute='_compute_amounts', store=True)
amount_tax = fields.Float(string='Tax Amount', compute='_compute_amounts', store=True)
amount_total = fields.Float(string='Total Amount', compute='_compute_amounts', store=True)
@api.depends('invoice_line_ids.price_subtotal', 'invoice_line_ids.price_tax')
def _compute_amounts(self):
for invoice in self:
invoice.amount_untaxed = sum(invoice.invoice_line_ids.mapped('price_subtotal'))
invoice.amount_tax = sum(invoice.invoice_line_ids.mapped('price_tax'))
invoice.amount_total = invoice.amount_untaxed + invoice.amount_taxDebugging Dependencies
When your computed fields aren't updating as expected, check these common issues:
- Verify all dependent fields are listed in @api.depends
- Ensure field names are spelled correctly
- Check that related field paths are valid
- Confirm that the compute method is properly iterating through records
- Look for any exceptions being raised within the compute method
To read more about How to Add a Computed Field in Odoo 19, refer to our blog How to Add a Computed Field in Odoo 19.