OD313 private-button-method¶
A button calls a private method, which Odoo refuses.
| 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 x starts with
_, whether or not the model has it, where BaseModel makes x private in the module's series,
or where any class in scope of the model at the button's position, or of a model it inherits,
decorates x with @api.private.
| Series | Private methods of BaseModel |
|---|---|
| 10.0 on | init |
| 16.0, 17.0, saas~17.4 on | concat, ensure_one, filtered, filtered_domain, flush_model, flush_recordset, invalidate_model, invalidate_recordset, mapped, new, sorted, union, with_company, with_context, with_env, with_prefetch, with_user |
| 17.0, saas~17.4 on | grouped |
| 18.0 on | sudo |
| saas~18.2 on | browse, check_access, exists, fetch, modified, search_fetch, update |
| saas~18.3 on | lock_for_update |
| 19.0 on | try_lock_for_update |
A module overriding one of these names is still reported, with or without the decorator. A module
of unknown series is reported only for init.
Why it matters¶
Odoo calls a button through RPC, which refuses a private method. From saas~13.1 it checks a name
starting with _ when it loads the view, so the module fails to install; before that, the button
fails when a user clicks it. A demo file differs: see load
failures. @api.private is in 16.0, 17.0
and from saas~17.4 on, not in saas~16.1 to 16.4 or saas~17.1 to 17.3, and is refused the same way,
on any override of the method. 16.0 and 17.0 gained it by a February 2025 backport, so an older
build accepts a button on ensure_one; the series cannot tell them apart, and the rule reports it.
From saas~18.3 the install check reads only the top override, and the click still refuses any.
init is refused from 10.0 on, by name until @api.private takes over, and 9.0 refuses only the
leading _.
The rule skips the same views and positions as OD312, but not the modules it skips for
lack of classes: a leading _ or a private method of BaseModel tells the method is private
without looking it up.
Example¶
_action_confirm is private. Add a public method for the button to call.