Computed Fields in Odoo 18
By Braincuber Team
Published on December 26, 2025
Developers hardcoding calculations across multiple form views create maintenance nightmares: total price calculated in template, duplicated in report, copy-pasted in email, each using slightly different formulas. When tax rate changes, you update calculations in 12 places and miss 3, causing invoice discrepancies. Users see stale "Total" values because nothing triggers recalculation when underlying values change.
Odoo 18's Computed Fields centralize calculation logic in model methods that automatically execute when dependencies change. Define total_price once as quantity × unit_price, decorate with @api.depends to track dependencies, and Odoo updates values automatically across all views when either input changes—no duplication, no manual refresh, instant consistency everywhere the field appears.
Computed Fields Benefits:
- Automatic recalculation when dependencies change
- Single source of truth for calculation logic
- No manual refresh required
- Database storage optional (store=True)
- Searchable and filterable when stored
- Performance optimization through caching
- Complex business rules centralized
Understanding Computed Fields
Computed fields calculate values dynamically using Python methods instead of storing static user input.
Computed vs Regular Fields
| Aspect | Regular Field | Computed Field |
|---|---|---|
| Value Source | User input or default | Python method calculation |
| Storage | Always stored in database | Optional (store=True/False) |
| Editability | Editable by default | Read-only (unless inverse function) |
| Update Trigger | Manual user edit | Automatic when dependencies change |
| Use Case | Name, email, quantity | Total price, age, status |
Creating a Computed Field
Basic Structure
Computed fields require three components: field definition with compute parameter, computation method, and dependency declaration.
from odoo import models, fields, api
class OrderLine(models.Model):
_name = 'sale.order.line'
_description = 'Sales Order Line'
product_id = fields.Many2one('product.product', string='Product')
quantity = fields.Float(string='Quantity', default=1.0)
unit_price = fields.Float(string='Unit Price')
# Computed field with dependencies
total_price = fields.Float(
string='Total Price',
compute='_compute_total_price',
store=True
)
@api.depends('quantity', 'unit_price')
def _compute_total_price(self):
for record in self:
record.total_price = record.quantity * record.unit_price
Code Breakdown
Field Definition
compute='_compute_total_price'
Points to method that calculates value
Storage Parameter
store=True
Saves computed value to database for searching/filtering
Dependency Decorator
@api.depends('quantity', 'unit_price')
Tracks which fields trigger recalculation
Computation Method
def _compute_total_price(self)
Calculates value for each record in recordset
The @api.depends Decorator
What @api.depends Does
The decorator tells Odoo's ORM which fields to monitor. When any dependency changes, Odoo automatically calls the compute method to update the computed field.
WITH @api.depends
- Automatic updates when dependencies change
- Values always current across all views
- Stored values (store=True) stay synchronized
WITHOUT @api.depends
- Only calculates on record creation/save
- Becomes stale when dependencies change
- Data inconsistency across system
Example Without @api.depends
class OrderLine(models.Model):
_name = 'sale.order.line'
quantity = fields.Float(string='Quantity')
unit_price = fields.Float(string='Unit Price')
total_price = fields.Float(
string='Total Price',
compute='_compute_total_price',
store=True
)
# MISSING @api.depends decorator!
def _compute_total_price(self):
for record in self:
record.total_price = record.quantity * record.unit_price
# Problem: When quantity or unit_price changes,
# total_price does NOT update automatically
The store=True Parameter
Storage Options
| Parameter | Behavior | Pros | Cons |
|---|---|---|---|
| store=True | Value saved to database | Searchable, filterable, faster reads | Database space, write overhead |
| store=False (default) |
Calculated on-the-fly | No storage cost, always fresh | Can't search/filter, slower reads |
When to Use store=True
Use store=True When:
- Field appears in search filters or list views
- Users need to sort records by this field
- Calculation is expensive (complex queries, external APIs)
- Field accessed frequently (reports, dashboards)
- Need to aggregate values (sum, average, count)
Skip store=True When:
- Simple calculation (addition, concatenation)
- Field only displayed, never searched/filtered
- Dependencies change frequently (value always stale)
- Working with millions of records (storage overhead)
Advanced Examples
Multi-Field Dependencies
class Invoice(models.Model):
_name = 'account.invoice'
subtotal = fields.Float(string='Subtotal')
tax_rate = fields.Float(string='Tax Rate (%)', default=10.0)
discount_percent = fields.Float(string='Discount (%)', default=0.0)
tax_amount = fields.Float(
string='Tax Amount',
compute='_compute_amounts',
store=True
)
discount_amount = fields.Float(
string='Discount Amount',
compute='_compute_amounts',
store=True
)
grand_total = fields.Float(
string='Grand Total',
compute='_compute_amounts',
store=True
)
@api.depends('subtotal', 'tax_rate', 'discount_percent')
def _compute_amounts(self):
for record in self:
# Calculate discount
record.discount_amount = (
record.subtotal * record.discount_percent / 100
)
# Calculate tax on discounted amount
taxable_amount = record.subtotal - record.discount_amount
record.tax_amount = taxable_amount * record.tax_rate / 100
# Calculate grand total
record.grand_total = (
record.subtotal -
record.discount_amount +
record.tax_amount
)
Relational Dependencies
class Project(models.Model):
_name = 'project.project'
task_ids = fields.One2many('project.task', 'project_id', string='Tasks')
# Computed from related records
total_tasks = fields.Integer(
string='Total Tasks',
compute='_compute_task_stats',
store=True
)
completed_tasks = fields.Integer(
string='Completed Tasks',
compute='_compute_task_stats',
store=True
)
completion_percent = fields.Float(
string='Completion %',
compute='_compute_task_stats',
store=True
)
# Depend on related model fields
@api.depends('task_ids', 'task_ids.state')
def _compute_task_stats(self):
for project in self:
project.total_tasks = len(project.task_ids)
project.completed_tasks = len(
project.task_ids.filtered(lambda t: t.state == 'done')
)
if project.total_tasks > 0:
project.completion_percent = (
project.completed_tasks / project.total_tasks * 100
)
else:
project.completion_percent = 0.0
Best Practices
Always Use @api.depends
Declare ALL dependencies, even if obvious. Missing dependencies cause stale data that's hard to debug.
Loop Through Records
Always use for record in self: pattern—compute methods receive recordsets, not single records.
Optimize Performance
For expensive calculations accessed frequently, use store=True to cache results instead of recalculating.
Handle Empty Recordsets
Check for division by zero or empty collections before calculation to prevent errors.
Frequently Asked Questions
@api.depends('task_ids.state') tells Odoo to recompute when any task's state changes. Works across Many2one, One2many, Many2many relationships.
inverse='_inverse_method_name' on field, then create method that sets dependency values when computed field is manually edited. Useful for bidirectional calculations.
Conclusion
Odoo 18's computed fields eliminate duplicated calculation logic by centralizing business rules in model methods that execute automatically when dependencies change. By decorating methods with @api.depends to track field relationships and using store=True for frequently accessed values, you create maintainable code where calculations update consistently across all views, reports, and integrations without manual intervention.
Whether calculating invoice totals from line items, deriving project completion percentages from task states, or aggregating complex financial metrics, computed fields transform scattered formulas into single sources of truth. The small upfront investment in properly declaring dependencies prevents the debugging nightmares of stale data, ensuring users always see accurate, current values regardless of which view or report they access.
Need Help with Odoo Development?
Our Python developers can help you design computed field strategies, optimize performance with proper dependencies, implement complex business logic, create inverse functions for editable computes, and build complete Odoo modules with best-practice architecture.
