Skip to content

Odoo Related Field Patterns: Delegation and Data Access

DeployMonkey Team · March 24, 2026 10 min read

What Are Related Fields?

Related fields in Odoo provide a shortcut to access data from linked records. Instead of writing a compute method to read a value from a Many2one relation, you declare the field path and Odoo handles the rest. They are syntactic sugar for simple delegation patterns.

Basic Syntax

class SaleOrderLine(models.Model):
    _name = 'sale.order.line'

    order_id = fields.Many2one('sale.order')
    
    # Related fields — access parent record data
    partner_id = fields.Many2one(
        related='order_id.partner_id',
        string='Customer',
    )
    order_date = fields.Date(
        related='order_id.date_order',
        string='Order Date',
    )
    company_id = fields.Many2one(
        related='order_id.company_id',
        store=True,
    )

The related attribute takes a dot-notation path through Many2one fields to reach the target field value.

Stored vs Non-Stored Related

Non-Stored (Default)

partner_name = fields.Char(related='partner_id.name')
  • No database column created
  • Reads from the related record on every access
  • Always up to date
  • Cannot be searched or filtered
  • Cannot be used in group_by

Stored

partner_name = fields.Char(related='partner_id.name', store=True)
  • Creates a database column with the value
  • Updated automatically when the source changes
  • Searchable and usable in group_by
  • Can have an index for faster queries
  • Takes storage space

Multi-Level Related

Follow chains of Many2one fields:

# Access grandparent data
partner_country = fields.Char(
    related='order_id.partner_id.country_id.name',
    string='Customer Country',
)

# Three levels deep
partner_country_code = fields.Char(
    related='order_id.partner_id.country_id.code',
)

Each level must be a Many2one field (not One2many or Many2many) until the final field, which can be any type.

Readonly Behavior

By default, related fields are readonly. Writing to them is possible but not recommended:

# Readonly by default (recommended)
partner_email = fields.Char(related='partner_id.email')

# Writable related field (modifies the source record!)
partner_email = fields.Char(
    related='partner_id.email',
    readonly=False,
)

Setting readonly=False means writing to this field actually modifies the related partner's email. This can cause unexpected data changes. Use with extreme caution.

Related vs Compute

FeatureRelatedCompute
SyntaxDeclarative (one line)Imperative (method)
LogicSimple field path onlyAny Python logic
TransformationNo transformationCan transform values
Multiple sourcesSingle pathCan combine multiple fields
Fallback valuesEmpty if path is brokenCan set defaults
PerformanceOptimized internallyStandard compute overhead

When to Use Related

  • Simple field delegation — showing a parent record's field value
  • No transformation needed — the value is used as-is
  • Single source — one path to one field

When to Use Compute Instead

# Need transformation — use compute
discount_display = fields.Char(compute='_compute_discount_display')

@api.depends('discount_percent')
def _compute_discount_display(self):
    for record in self:
        record.discount_display = f"{record.discount_percent}% off"

# Need fallback — use compute
contact_name = fields.Char(compute='_compute_contact_name')

@api.depends('partner_id.name', 'manual_name')
def _compute_contact_name(self):
    for record in self:
        record.contact_name = record.manual_name or record.partner_id.name or 'Unknown'

Common Patterns

Company from Parent

company_id = fields.Many2one(
    'res.company',
    related='order_id.company_id',
    store=True,
    index=True,
)

Storing and indexing company_id is important for multi-company record rules performance.

Currency from Pricelist

currency_id = fields.Many2one(
    'res.currency',
    related='pricelist_id.currency_id',
)

Common Pitfalls

  • Broken paths — If any Many2one in the chain is empty, the related field returns False/empty. No error is raised.
  • Stored related + source changes — Stored related fields update when the source changes, but this triggers a write on every record that references the changed source. For widely-referenced records (like company settings), this can cascade to thousands of writes.
  • Writable related fields — Setting readonly=False modifies the source record, which may surprise users. The change affects all records that reference the same source.
  • Index missing — Stored related fields used in searches should have index=True for performance.
  • One2many/Many2many chains — Related fields cannot traverse One2many or Many2many fields (only Many2one). Use compute fields for those cases.