2025, Dec 09 17:00

Make Odoo computed fields update instantly on new records with @api.onchange (and when to use related fields)

Resolve Odoo forms where a computed field stays empty until save. Learn when to use @api.onchange vs @api.depends, and prefer related fields for mirrors.

When building Odoo forms, it’s common to mirror data from a parent record for clarity. A typical case is displaying an equipment name from a related job request. The surprise comes on new records: a computed Char field looks fine on paper but stays empty in the UI until the record is saved. Here’s why it happens and how to make it update immediately when the user changes the parent.

Problem setup

The goal is to show a display-only field that mirrors a value from the selected job request. The field is computed and not stored.

from odoo import api, fields, models

class AssetLine(models.Model):
    _name = 'asset.line'

    parent_ref = fields.Many2one('job.request', string='Request', required=True)
    asset_label = fields.Char('Nama Asset', store=False, compute='_calc_label')

    @api.depends('parent_ref')
    def _calc_label(self):
        for rec in self:
            req = rec.parent_ref
            rec.asset_label = req.equipment_id.name

On the form view, selecting a request does not populate the label for new records. The value is empty even though the logic is straightforward.

What’s going on

The compute method tied to depends is evaluated when values are written to the database. On a brand-new form that hasn’t been saved, the UI interaction alone doesn’t guarantee the compute runs. That’s why the field appears blank while the user is still filling the form. In this situation, the parent might be unset, which effectively makes the expression resolve to False, and the label remains empty. Once you save, the compute does kick in, which is why it works on existing data.

For new records (and dynamic changes on the web!) you want to use onchange; always assume someone will create a record in code somewhere somehow so also make sure you keep your depends.

Solution

Trigger the calculation in the browser as the user changes the parent field, and keep the dependency for correctness in code or on save. The combination of onchange for interactivity and depends for consistency covers both paths.

from odoo import api, fields, models

class AssetLine(models.Model):
    _name = 'asset.line'

    parent_ref = fields.Many2one('job.request', string='Request', required=True)
    asset_label = fields.Char('Nama Asset', store=False, compute='_calc_label')

    @api.onchange('parent_ref')
    @api.depends('parent_ref')
    def _calc_label(self):
        for rec in self:
            req = rec.parent_ref
            rec.asset_label = req.equipment_id.name

If all you need is a direct mirror with no extra logic, a related field is an even simpler path. It directly follows the parent chain and updates accordingly without custom code.

from odoo import fields, models

class AssetLine(models.Model):
    _name = 'asset.line'

    parent_ref = fields.Many2one('job.request', string='Request', required=True)
    asset_label = fields.Char(related='parent_ref.equipment_id.name')

Why this matters

Relying on depends alone can lead to empty fields on fresh forms, pushing users to guess and double-check data after saving. Adding onchange provides immediate feedback while editing, and keeping depends ensures the value is correct when records are created or modified in code. As soon as you save, the compute will also run, but users shouldn’t have to wait for that to see what they’re selecting.

Takeaways

For fields that must reflect a parent selection in real time on a new record, use onchange alongside depends. If the field is purely a passthrough of a parent attribute, prefer a related field for a more concise and maintainable model. This small adjustment eliminates confusion, removes guesswork before form submission, and keeps data consistent no matter how records are created.