Skip to content

Fix Odoo Stored Computed Field Not Updating: Stale Values and Recomputation Issues

DeployMonkey Team · March 23, 2026 11 min read

The Problem

You have a stored computed field in Odoo that should update when its dependencies change, but it stubbornly shows the old value. The formula is correct, the dependencies are listed in @api.depends(), but the field just will not recompute. This is a subtle and common issue that affects both standard and custom Odoo modules.

Common Symptoms

  • Computed field shows stale/old value after dependency changes
  • Field updates when you edit and save the record but not when dependencies change via code
  • After module upgrade (-u), computed fields still show old values
  • Field value correct for new records but wrong for existing ones
  • Computed field based on One2many lines not updating when lines change

How Stored Computed Fields Work

When a stored computed field has @api.depends('field_a', 'field_b'):

  1. Odoo monitors writes to field_a and field_b
  2. When those fields change via write(), Odoo marks the computed field for recomputation
  3. At the end of the current flush cycle, Odoo calls the compute method
  4. The new value is stored in the database

If any step in this chain breaks, the field does not update.

Causes and Fixes

1. Missing or Incomplete @api.depends()

The most common cause. If a dependency is not listed, changes to it will not trigger recomputation.

# BAD: missing dependency on 'discount'
@api.depends('price', 'quantity')
def _compute_total(self):
    for rec in self:
        rec.total = rec.price * rec.quantity * (1 - rec.discount / 100)
        # 'discount' changes will NOT trigger recomputation!

# GOOD: all dependencies listed
@api.depends('price', 'quantity', 'discount')
def _compute_total(self):
    for rec in self:
        rec.total = rec.price * rec.quantity * (1 - rec.discount / 100)

2. Dependency on One2many Sub-fields

For fields that depend on One2many child records, you must use dot notation to specify which child field triggers recomputation.

# BAD: does not trigger when line amounts change
@api.depends('line_ids')
def _compute_total(self):
    for rec in self:
        rec.total = sum(rec.line_ids.mapped('amount'))

# GOOD: triggers when any line's amount changes
@api.depends('line_ids.amount')
def _compute_total(self):
    for rec in self:
        rec.total = sum(rec.line_ids.mapped('amount'))

Note: @api.depends('line_ids') only triggers when lines are added or removed, not when existing line fields change.

3. Direct SQL Updates Bypass Recomputation

If a dependency field is updated via raw SQL instead of ORM write(), Odoo has no way to know the field changed.

# BAD: bypasses ORM, no recomputation triggered
self.env.cr.execute(
    "UPDATE sale_order SET discount = 10 WHERE id = %s", [order_id]
)

# GOOD: uses ORM, triggers recomputation
order = self.env['sale.order'].browse(order_id)
order.write({'discount': 10})

4. Module Upgrade Does Not Recompute Existing Values

Running -u my_module updates the field definition but does NOT recompute values for existing records. If you change the compute method logic, old records keep their old values.

Fix: Force recomputation after upgrade:

# Option 1: In a post-init hook or migration script:
def post_init_hook(env):
    records = env['my.model'].search([])
    records._compute_my_field()  # force recompute

# Option 2: Via Odoo shell:
records = env['my.model'].search([])
records._compute_my_field()
env.cr.commit()

# Option 3: Via SQL + ORM recompute:
env['my.model'].search([])._recompute_todo(env['my.model']._fields['my_field'])
env['my.model'].recompute()
env.cr.commit()

5. Compute Method Raises Exception Silently

If the compute method raises an exception for some records, Odoo may skip those records without updating their values.

Fix: Add error handling in the compute method:

@api.depends('partner_id.country_id')
def _compute_tax_rate(self):
    for rec in self:
        try:
            rec.tax_rate = rec.partner_id.country_id.tax_rate or 0
        except Exception:
            rec.tax_rate = 0  # safe default

6. Cache Showing Stale Values

Odoo's ORM cache may show old values even after recomputation, especially in the same transaction.

Fix:

# Invalidate cache:
self.env.invalidate_all()

# Or invalidate specific fields:
self.invalidate_recordset(['my_computed_field'])

# Then re-read:
record = self.env['my.model'].browse(record_id)
print(record.my_computed_field)  # fresh value

7. depends on Related Field Chain

Long dependency chains through related fields may not always trigger correctly, especially with deep nesting.

# Potentially unreliable for deep chains:
@api.depends('order_id.partner_id.country_id.code')
def _compute_country_code(self):
    ...

# More reliable: use a related field as intermediary
country_code = fields.Char(
    related='order_id.partner_id.country_id.code', 
    store=True
)

8. Field Not Marked as store=True

Non-stored computed fields are never written to the database. They are computed on-the-fly for each access. If you expect persistence, you need store=True.

# Non-stored: computed every time, never saved
total = fields.Float(compute='_compute_total')  # no persistence

# Stored: saved to database, updated on dependency change
total = fields.Float(compute='_compute_total', store=True)

Force Recomputation of All Records

# In Odoo shell — force recompute for all records:
Model = env['my.model']
records = Model.search([])

# Method 1: call compute directly
records._compute_my_field()

# Method 2: trigger write on dependency
# (causes natural recomputation)
for rec in records:
    rec.write({'dependency_field': rec.dependency_field})

# Method 3: SQL + invalidate
env.cr.execute("UPDATE my_model SET my_field = NULL")
env.invalidate_all()
records._compute_my_field()

env.cr.commit()

Best Practices

  • List ALL dependencies in @api.depends() — missing one is the top cause of stale fields
  • Use dot notation for One2many dependencies: line_ids.amount
  • Never use raw SQL to update fields that are dependencies of computed fields
  • After changing compute logic, force recompute on existing records
  • Add error handling in compute methods to prevent silent failures
  • Use related fields for simple dependency chains instead of custom compute methods