Computed Fields Causing Infinite Recomputations (odoo)
Source: Dev.to
Problem Statement
Poorly designed computed fields trigger unnecessary recomputations or infinite loops due to incorrect dependency declarations (@api.depends), leading to performance issues and unstable behavior.
Step 1 – Identify the problematic computed field
Typical symptoms
- Form view never finishes loading
- CPU spikes when opening records
- Logs show repeated recomputation
- Server freezes after record creation/update
Enable logs
--log-level=debug
Look for repeating messages
Computing field x_field_name
Step 2 – Identify common WRONG patterns (most bugs come from here)
1. Computed field depends on itself
@api.depends('total_amount')
def _compute_total_amount(self):
for rec in self:
rec.total_amount = rec.price * rec.qty
Result: infinite recomputation.
2. Writing to other fields inside compute
@api.depends('price', 'qty')
def _compute_total(self):
for rec in self:
rec.total = rec.price * rec.qty
rec.subtotal = rec.total * 0.9 # BAD
3. Using write() inside compute
@api.depends('line_ids.amount')
def _compute_amount(self):
for rec in self:
rec.write({'total': sum(rec.line_ids.mapped('amount'))})
Result: recursive recompute + database writes.
Step 3 – Define correct dependencies ONLY
@api.depends('price', 'qty')
def _compute_total(self):
for rec in self:
rec.total = rec.price * rec.qty
Rules
- Depend only on source fields.
- Never depend on the computed field itself.
- Avoid
@api.depends('*').
Step 4 – Never modify other fields inside compute
Separate computed fields
total = fields.Float(compute='_compute_total', store=True)
subtotal = fields.Float(compute='_compute_subtotal', store=True)
@api.depends('price', 'qty')
def _compute_total(self):
for rec in self:
rec.total = rec.price * rec.qty
@api.depends('total')
def _compute_subtotal(self):
for rec in self:
rec.subtotal = rec.total * 0.9
Benefits: clean, predictable, no loops.
Step 5 – Use store=True ONLY when required
BAD (unnecessary storage)
total = fields.Float(compute='_compute_total', store=True)
Use store=True only if the field is:
- Frequently searched
- Used for grouping or reporting
GOOD (transient UI value)
total = fields.Float(compute='_compute_total')
store=True increases recomputation cost.
Step 6 – Avoid ORM writes in compute (use @api.onchange instead)
WRONG (compute)
@api.depends('qty')
def _compute_price(self):
for rec in self:
rec.price = rec.qty * 10
RIGHT (onchange)
@api.onchange('qty')
def _onchange_qty(self):
self.price = self.qty * 10
Use onchange for UI‑only logic – no recomputation loops.
Step 7 – Use @api.depends_context when needed
If the computation depends on company, language, or user:
@api.depends_context('company')
@api.depends('amount')
def _compute_tax(self):
for rec in self:
rec.tax = rec.amount * rec.company_id.tax_rate
Prevents unnecessary recomputation.
Step 8 – Fix One2many / Many2one loops
COMMON LOOP
@api.depends('line_ids.total')
def _compute_total(self):
for rec in self:
rec.total = sum(rec.line_ids.mapped('total'))
If line.total depends back on the parent → infinite loop.
SAFE VERSION
@api.depends('line_ids.price', 'line_ids.qty')
def _compute_total(self):
for rec in self:
rec.total = sum(line.price * line.qty for line in rec.line_ids)
Step 9 – Use SQL constraints instead of compute for validation
BAD (compute for validation)
@api.depends('qty')
def _compute_check(self):
if self.qty < 0:
raise ValidationError("Invalid")
GOOD (constraint)
@api.constrains('qty')
def _check_qty(self):
for rec in self:
if rec.qty < 0:
raise ValidationError("Quantity cannot be negative")
Step 10 – Final Safe Computed Field Template (Best Practice)
total = fields.Float(
compute='_compute_total',
store=True,
)
@api.depends('price', 'qty')
def _compute_total(self):
for rec in self:
rec.total = rec.price * rec.qty if rec.price and rec.qty else 0.0
Key points
- No writes inside the compute method.
- No recursion.
- Minimal, correct dependencies.
- Safe for production.
Conclusion
In Odoo, infinite recomputation issues are almost always caused by impure computed fields that write data, depend on themselves, or have poorly defined dependencies. The fix is simple but strict:
- Keep compute methods pure.
- Declare minimal and accurate dependencies.
- Avoid ORM writes inside computes; use
@api.onchangeor constraints instead. - Separate UI‑only logic from stored logic.
Applying these rules eliminates loops, improves performance, and makes your Odoo modules stable and maintainable.
Calculations instead of business logic containers, performance stabilizes, recomputation stops, and your Odoo system becomes predictable and scalable.