Skip to content

OD230 orphan-hook-method

A compute, inverse, search or onchange method of a model extension is named nowhere, so Odoo never runs it.

Category Severity Series Needs the index
Correctness Info all yes

What it reports

A method of a class that extends its model in place, with _inherit and no other _name, whose name starts with _compute_, _inverse_, _search_, _onchange_ or onchange_, when:

  • no other class of the model or of a model it inherits defines it, nor BaseModel;
  • no file of any indexed module names it outside the def itself, comments included: a Python, XML or CSV file anywhere in a module, or a script or template under its static/src;
  • its decorators, if any, are among api.depends, api.depends_context, api.model, api.multi, api.one and api.returns;
  • Odoo could call it with what it passes: the operator and the value for a _search_ method, by position or through *args, nothing for a _compute_, _inverse_ or _onchange_ one, and anything for an onchange_ one;
  • the class binds its name once.

A name built at runtime counts by its literal part: "_compute_%s" % name, f"_compute_{name}", `_compute_${name}` and "_compute_" + name name every method that starts with _compute_.

Why it matters

Odoo runs such a method only by the name a field option gives it (compute, inverse or search), through the @api.onchange decorator on the method itself, or, through 11.0, by the on_change attribute of a view field, which spells the call with its arguments. An override that upstream renamed, or whose field upstream dropped, becomes dead code: the change it was written for no longer happens, and nothing fails to say so.

A method called by a name the text of the scan never spells, such as one read from the database, is reported. A super() call of the method spells its name, so OD211 reports that override instead. The rule leaves out:

  • the methods of a new model, whose hooks are its own to leave unused;
  • a method with another decorator, which may register it elsewhere;
  • a method that requires arguments Odoo does not pass, a helper that only shares the prefix;
  • every method where OD211 is silent;
  • every method of a model when a class of it, or of a model it inherits, has a plain Python class among its bases, whose methods the scan cannot read; a base its file defines above that class with nothing but methods and literals in its body is read, and defines the names it binds;
  • every method of a module when it, a module it depends on or one that depends on it has a file the scan cannot read or decode, which may name the method.

A script under static/tests does not count as naming a method, since Odoo loads it only to run the tests.

Example

models/account_move.py
class AccountMove(models.Model):
    _inherit = "account.move"

    @api.depends("journal_id", "sequence_number")
    def _compute_made_sequence_hole(self):
        for move in self:
            move.made_sequence_hole = move.journal_id.code != "IMP" and move.sequence_number > 1

account 17.0 computes made_sequence_hole with _compute_made_sequence_hole. 18.0 replaces the field with made_sequence_gap, computed by _compute_made_sequence_gap, so the override no longer runs.

models/account_move.py
class AccountMove(models.Model):
    _inherit = "account.move"

    @api.depends("journal_id", "sequence_number")
    def _compute_made_sequence_gap(self):
        for move in self:
            move.made_sequence_gap = move.journal_id.code != "IMP" and move.sequence_number > 1

Suppressing

def _compute_made_sequence_hole(self):  # odooscan: ignore[OD230]

See suppressing a finding.