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
| Feature | Related | Compute |
|---|---|---|
| Syntax | Declarative (one line) | Imperative (method) |
| Logic | Simple field path only | Any Python logic |
| Transformation | No transformation | Can transform values |
| Multiple sources | Single path | Can combine multiple fields |
| Fallback values | Empty if path is broken | Can set defaults |
| Performance | Optimized internally | Standard 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.