Skip to content

OD123 patch-at-import

Code run at import patches Odoo, for every database the server runs.

Category Severity Series Needs the index
Correctness Warning all no

What it reports

A statement at the top level of a file, or in an if, try or with block there, that sets an attribute on a name the file imports from odoo or from a module under it: models.BaseModel._x = ..., fields.Field.x += ..., misc.x = ... after from odoo.tools import misc, SaleOrder._x = ... after from odoo.addons.sale.models.sale_order import SaleOrder, or setattr(fields.Field, "x", ...).

A patch inside a function, such as a post_load hook, is not reported, and neither is one on a module the file imports relatively. Neither are a docstring set through __doc__ nor a module named under odoo.addons, with odoo.addons.x = ... or through sys.modules, since they change no behaviour.

The message advises inheriting the model when the patched class is a model class: one the index finds as a model class in the file the import names, such as SaleOrder in sale/models/sale_order.py. When the addon or that file is not indexed, a class under the addon's models, wizard, wizards or report package counts as one, and so do BaseModel, AbstractModel, Model, TransientModel and CachedModel from odoo.models or odoo.orm. Another class gets the advice to subclass it, and a module the advice to keep the value in the module itself.

Why it matters

Odoo imports each module once per process, at startup for a server-wide module and otherwise the first time a database that has it installed loads, and every database the server runs then shares the classes and modules it imported. An attribute set at import therefore changes the ORM for all of them, including the databases where the module is not installed, and only from the moment the first one loaded: a database's behaviour depends on which other databases the worker served before.

A model class that inherits the model changes it only in the registries of the databases that have the module installed. Override the method there and call super(). For another class, use a subclass of it in the module's own code, and keep a value the module needs in the module itself.

Example

models/res_partner.py
from odoo import models


def _x_greeting(self):
    return f"Hello {self.display_name}"


models.BaseModel._x_greeting = _x_greeting

Every model of every database on the server gains _x_greeting(), with this module installed there or not.

models/res_partner.py
from odoo import models


class ResPartner(models.Model):
    _inherit = "res.partner"

    def _x_greeting(self):
        return f"Hello {self.display_name}"

Suppressing

models.BaseModel._x_greeting = _x_greeting  # odooscan: ignore[OD123]

See suppressing a finding.