Skip to content

OD312 unknown-button-method

A button calls a method its model does not have.

Category Severity Series Needs the index
Correctness Error all yes

What it reports

A <button type="object" name="x"> in a view arch, with a literal name, where neither the classes of the model, those of the models it inherits, nor BaseModel define x, or where only a module missing from depends adds it. The model is the one at the button's position: the view's, the comodel of the x2many field whose subview encloses it, also when an extension adds the subview directly inside a field of the parent view, or the comodel of the <groupby> around it. An extension shows only part of that position, so a button it places in a subview can be read against the view's model: see why it matters. A module that Odoo always installs with base, such as web, counts as in depends. A method of a model reached through _inherits or delegate=True does not count, since the model gets that model's fields and not its methods. A BaseModel method counts in every series, whichever series has it, but one BaseModel lost before saas~11.5, such as create_workflow, counts only in the series that have it, and in none for a module of unknown series.

A class defines a method with a def, in its body or under an if, try or with block there, or with a class attribute whose value may be a method: a name, an attribute, a lambda, a conditional expression, a subscript, or a call such as partialmethod(...), functools.partial(...) or api.model(f). A field, as OD210 counts one, a models.Constraint, an Index or a UniqueIndex defines none, and neither does plain data: a literal, an f-string, a list, a tuple, a dict, a set or a comprehension. A button on limit = 3 is reported, as is one on a field. Under a block the rule reads each def and alias but not the body of a method, and a field there is not read.

Why it matters

From saas~13.1 Odoo validates the button when it loads the view, so a misspelled method stops the install or the upgrade of the module. A demo file differs: see load failures. Before saas~13.1 the view loads and the button fails when a user clicks it. A method that a module outside depends adds exists only where that module happens to be installed.

The rule stays silent when it cannot see every class or tell the model: a module whose dependencies include one that is not indexed, such as an Enterprise module, a module that builds model names at runtime, a model that is unknown or outside depends (OD201 and OD202 report those), and a class of the model, or of a model it inherits, with a plain Python base or an _inherit no indexed module defines, or in a file that does not parse, which counts as one the scan cannot read. Such a class of a model reached through _inherits or delegate=True does not count, since those methods stay on that model. It also leaves alone a view whose root is QWeb or is not indexed, a button with special or position, and a button an extension anchors on or under a list, tree, kanban or column it does not reach through a field, since that anchor may sit in the main view or in a subview: an xpath step with no axis, as in //list or //list/button, or a spec with position, as in <list position="inside">.

An extension can place a button in a subview, and the rule reads the extension without its parent view, so a correct button can be reported when the rule takes that subview for the main view. An anchor is the element a spec locates, and the rule reads the buttons the spec adds there. It follows the field an xpath names on the way to the anchor, or the field a spec adds a subview inside, and reads the button against that field's comodel. It stays silent where it cannot tell the model: on the list, tree, kanban or column anchors above, and in a subview added inside a field when a subview of the inheritance tree is on a model that may have that field too, since a spec locates the first field of that name anywhere. Otherwise it reads the view's model. Name the field on the way in, as in //field[@name='line_ids']//button[@name='action_split'], and suppress the finding where no such anchor exists.

A private name is OD313's. Names built at runtime, as with t-att-name, are not checked.

Example

views/sale_order_views.xml
<button type="object" name="action_confirmm" string="Confirm"/>

sale.order has no action_confirmm, so the module fails to install.

views/sale_order_views.xml
<button type="object" name="action_confirm" string="Confirm"/>

Suppressing

<!-- odooscan: ignore[OD312] -->

See suppressing a finding.