Understanding @api.depends vs @api.onchange in Odoo 19

 



One of the most confusing topics for many Odoo developers — especially beginners — is the difference between @api.depends and @api.onchange.

At first glance, both seem to react when a field changes. But internally, they work very differently and are used for completely different purposes.

Understanding when to use each one is extremely important for writing clean, efficient, and bug-free Odoo modules.


What is @api.depends?

@api.depends is used with computed fields.

It tells Odoo:

“Recompute this field whenever these dependent fields change.”

The value is computed:

  • on the server side

  • automatically

  • and can optionally be stored in the database


Example of @api.depends

from odoo import models, fields, api

class Student(models.Model):
    _name = 'student.student'

    name = fields.Char()
    mark1 = fields.Float()
    mark2 = fields.Float()

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

    @api.depends('mark1', 'mark2')
    def _compute_total(self):
        for record in self:
            record.total = record.mark1 + record.mark2

How It Works

Whenever:

  • mark1

  • or mark2

changes, Odoo automatically recalculates total.

If store=True is used:

  • the value is stored in the database

  • searchable

  • usable in filters and reports

If store=False:

  • the value is calculated dynamically every time


What is @api.onchange?

@api.onchange is completely different.

It is used to dynamically update the form view when a user changes a field in the UI.

It only works:

  • inside forms

  • during user interaction

  • before saving the record

Most importantly:

@api.onchange does NOT store values automatically in the database.

Example of @api.onchange

from odoo import models, fields, api

class Student(models.Model):
    _name = 'student.student'

    age = fields.Integer()
    category = fields.Char()

    @api.onchange('age')
    def _onchange_age(self):
        if self.age < 18:
            self.category = 'Minor'
        else:
            self.category = 'Adult'

What Happens Here?

When the user changes the age field:

  • the form updates instantly

  • category changes immediately

  • no save is required

This improves user experience by giving live feedback.


Key Difference Between Them

Feature@api.depends@api.onchange
PurposeCompute field valuesUpdate UI dynamically
Runs OnServerClient/Form UI
Works Without FormYesNo
Works in Imports/APIYesNo
Stores ValuesYes (if store=True)No
Triggered During SaveYesNo
SearchableYes (stored fields)No
Best Use CaseBusiness logicUser interface assistance

Real-World Understanding

Use @api.depends When:

  • calculating totals

  • computing balances

  • generating statuses

  • creating business logic

  • handling automatic backend calculations

Examples:

  • invoice totals

  • stock quantities

  • salary calculations

  • contract balances


Use @api.onchange When:

  • showing warnings

  • auto-filling fields

  • updating domains

  • suggesting values

  • improving user experience

Examples:

  • autofill customer address

  • update payment terms

  • warning messages

  • dynamically filtering products


Important Mistake Developers Make

Many developers incorrectly use @api.onchange for business logic.

That is dangerous.

Why?

Because @api.onchange only runs in the form view.

It will NOT execute during:

  • XML-RPC/API calls

  • imports

  • automated scripts

  • cron jobs

  • backend operations

This means your logic may silently fail.


Bad Practice Example

@api.onchange('qty', 'price')
def _onchange_amount(self):
    self.total = self.qty * self.price

This looks correct in the UI.

But during imports or automated creation:

  • total may remain empty


Correct Approach

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

@api.depends('qty', 'price')
def _compute_total(self):
    for rec in self:
        rec.total = rec.qty * rec.price

This works everywhere consistently.


Can Both Be Used Together?

Yes — and many professional modules do exactly that.

A common pattern is:

  • @api.depends → handles actual business computation

  • @api.onchange → improves user experience in forms

This gives both:

  • reliable backend logic

  • smooth frontend interaction


Performance Considerations

@api.depends can become expensive if:

  • too many dependencies exist

  • computed fields trigger chains of recomputations

  • large datasets are involved

Best practices:

  • keep dependencies minimal

  • avoid unnecessary recomputations

  • use store=True carefully


Final Thoughts

Both @api.depends and @api.onchange are essential in Odoo development, but they solve completely different problems.

The golden rule is simple:

Use @api.depends for business logic.
Use @api.onchange for UI behavior.

Once developers clearly understand this distinction, their modules become:

  • more stable

  • easier to maintain

  • more scalable

  • and far less buggy.

Mastering these ORM decorators is one of the key steps toward becoming an advanced Odoo developer.

Comments