Skip to content

Odoo Stored vs Non-Stored Compute Fields: Performance and Use Cases

DeployMonkey Team · March 24, 2026 10 min read

Stored vs Non-Stored: The Trade-Off

Every compute field in Odoo faces a fundamental trade-off: store the result in the database (fast reads, write overhead) or calculate on every access (no storage, slower reads). Making the wrong choice causes either database bloat and slow writes, or sluggish list views and broken search functionality.

Non-Stored Compute Fields

Default Behavior

full_name = fields.Char(compute='_compute_full_name')

@api.depends('first_name', 'last_name')
def _compute_full_name(self):
    for record in self:
        record.full_name = f"{record.first_name or ''} {record.last_name or ''}".strip()

Characteristics

  • Calculated on every read (search, list view, form view, API call)
  • No database column — no storage space used
  • Always up to date — never stale
  • Cannot be searched with domain filters
  • Cannot be used in group_by or order_by
  • Cannot have tracking=True
  • Recalculation cost multiplied by number of records displayed

When to Use Non-Stored

  • Display-only fields that are never searched or filtered
  • Fields derived from non-stored sources (current time, session data)
  • Fields where the computation is very cheap
  • Fields on models with very few records

Stored Compute Fields

Syntax

total_amount = fields.Float(
    compute='_compute_total',
    store=True,
)

@api.depends('line_ids.quantity', 'line_ids.price_unit')
def _compute_total(self):
    for record in self:
        record.total_amount = sum(
            line.quantity * line.price_unit
            for line in record.line_ids
        )

Characteristics

  • Stored in the database as a regular column
  • Recomputed automatically when any dependency changes
  • Searchable with domain filters
  • Can be used in group_by and order_by
  • Can have tracking=True for chatter logging
  • Takes database storage space
  • Adds write overhead — extra UPDATE query on dependency changes

When to Use Stored

  • Fields that need to be searchable or filterable
  • Fields displayed in list views with many records
  • Fields used in reports and analytics
  • Fields that need chatter tracking
  • Fields with expensive computations (avoid recalculating on every read)

Performance Comparison

ScenarioNon-StoredStored
List view with 100 records100 computations per page load1 SQL query, no computation
Search/filterNot possibleNormal SQL WHERE clause
Write to dependencyNo overheadRecompute + UPDATE query
StorageZeroOne column per field
Data freshnessAlways currentUpdated on dependency write
Module upgradeNo action neededMay need recompute

precompute=True

An optimization for stored compute fields that depend only on values available at create time:

display_name = fields.Char(
    compute='_compute_display_name',
    store=True,
    precompute=True,
)

@api.depends('name', 'reference')
def _compute_display_name(self):
    for record in self:
        record.display_name = f"[{record.reference}] {record.name}"

With precompute=True, Odoo calculates the value during the INSERT (create) operation, avoiding a second UPDATE query. This only works if all dependencies are available at create time.

Stored Compute Recomputation

Module Upgrades

Stored compute fields are NOT automatically recomputed during -u module upgrades. If you change the computation logic, existing records keep their old values. Force recomputation:

# In a post-migration script
def migrate(cr, version):
    env = api.Environment(cr, SUPERUSER_ID, {})
    records = env['my.model'].search([])
    records._compute_total()  # Explicit recompute

Manual Recompute

# Recompute specific field on specific records
records = self.env['my.model'].search([])
records._compute_my_field()

# Or use the field's recompute method
self.env.add_to_compute(
    self.env['my.model']._fields['total_amount'],
    self.env['my.model'].search([]),
)

Common Pitfalls

  • Non-stored in list views — A non-stored compute field in a tree view with 1000 records means 1000 computations per page. Use store=True.
  • Searching non-stored fieldssearch([('computed_field', '=', value)]) silently returns wrong results or raises errors on non-stored fields.
  • Circular dependencies — Field A depends on B, B depends on A. Odoo detects some cycles but not all. Design your dependency graph carefully.
  • Module upgrade surprise — Changing compute logic does not update existing stored values. Always write migration scripts.
  • One2many dependency@api.depends('line_ids.amount') triggers recomputation when any line amount changes. For large datasets, this cascade can be expensive.