Skip to content

OD209 model-redefined

A class redefines a model that a module in depends already defines.

Category Severity Series Needs the index
Correctness Error all yes

What it reports

A class that introduces a model one of the module's dependencies already defines, without naming that model in _inherit: a class with _name, or one without it that Odoo names, as below. A class without _name whose _inherit has an entry that is not a string literal is read as extending its first literal entry, since the entries decide its name up to 18.0 and whether it is new from saas~18.1. A module that Odoo always installs with base, such as web, counts as in depends. Within the module itself, a second class that introduces the same model is reported when it comes after the first in the same file; the order of separate files is that of the imports, which the index does not record.

Why it matters

Odoo treats a class whose _name is not among its _inherit as the definition of a new model. A class without _name and with a list or tuple _inherit gets a name too. Up to 18.0, a one-entry list names the model the class extends, and a longer list the class name verbatim. From saas~18.1 the name is the class name, lowercased, with a dot before each capital that is not its first character and does not follow _, so ResPartner names res.partner whatever the list holds.

Odoo builds a fresh model from a class that defines one, the parents its _inherit names and base, and puts it in the registry in place of the existing one, so every field and method the earlier modules gave the model is gone. Their views and related fields that use a lost field then fail to load, their code fails when it reaches one, and the database columns behind the lost fields are left without a field.

Setting _inherit to the same name, or _inherit = _name, extends the model and is not reported. Neither is a model that only modules outside the dependencies define: two modules that do not depend on each other may each be installed alone.

Example

models/product_template.py
class ProductTemplate(models.Model):
    _name = "product.template"
    _description = "Product"

    warranty_months = fields.Integer()

product already defines product.template, so this class replaces the model instead of adding a field to it.

models/product_template.py
class ProductTemplate(models.Model):
    _inherit = "product.template"

    warranty_months = fields.Integer()

Suppressing

class ProductTemplate(models.Model):  # odooscan: ignore[OD209]

See suppressing a finding.