OD218 delegation-field-not-required¶
A field that delegates to a parent model is not required with ondelete cascade or restrict.
| Category | Severity | Series | Needs the index |
|---|---|---|---|
| Correctness | Warning, Error from saas~18.3 | all | yes |
What it reports¶
An entry of a class's _inherits that names a Many2one that is not both required and given
ondelete="cascade" or "restrict", reported at the entry, once per module. Up to 18.0 (before
saas~18.3), a Many2one with delegate=True that no _inherits entry names is held to the same,
and reported once per module, at its last definition that sets delegate, required or ondelete,
unless OD228 reports it. From saas~12.2 until saas~14.3, so in 13.0 and 14.0, a required
field with no ondelete, whether unset, None, False, zero or "", is not reported, and a field
that is not required is told to add required=True alone. The exception, from saas~13.1, is a field
of a model that is not transient to a comodel that refuses restrict, which still needs ondelete.
A model has the _inherits entries of the models it inherits with _inherit, so an entry of a
mixin names the field too, unless a later class gives the entry's parent model another field. Of the
models one class lists in _inherit, the first listed one's entry for a parent model wins (checked
in 12.0, 13.0 and 15.0 to 20.0), so a delegate=True field that only a later listed model's entry
for the same parent model names is held to the same rule as a lone one before saas~18.3.
The message names what the field lacks.
Why it matters¶
A model that delegates to a parent through _inherits stores each of its records alongside a record
of the parent, and reads the parent's fields through the delegating field. A record without a
parent, or a parent deleted from under it, breaks every one of those fields. Up to 18.0, Odoo forces
the field an _inherits entry names required with ondelete='cascade' and logs a warning, an unset
ondelete included. A delegate=True field that no entry names is forced required with a warning,
and an ondelete other than cascade or restrict is silently replaced with cascade. Either
way, the definition in the code no longer says what the database does. From saas~12.2 until
saas~14.3, so in 13.0 and 14.0, Odoo gives a field with no ondelete its default before it checks
the field, with no warning: restrict on a required field, or from saas~12.5 cascade on a
required field of a transient model to a regular one. The same series refuse a required field with
ondelete='set null' before they replace the value, which OD213 reports from 13.0. From saas~13.1
until saas~14.3, the default itself fails with ValueError when it is restrict and the comodel is
ir.model, ir.model.data, ir.model.fields, ir.model.fields.selection, ir.model.relation,
ir.model.constraint or ir.module.module. From saas~14.4 until saas~18.2, a delegate=True field
outside _inherits with no ondelete is worse: Odoo fails with AttributeError, which OD228
reports. From saas~18.3, so in 19.0, Odoo refuses the model, so the module cannot be installed and a
database that has it does not start.
The field takes each attribute from the last definitions that set it, in this module or in its
dependencies, so a required or an ondelete given in another class counts. An attribute is
unknown when it is not a literal, a definition unpacks **kwargs, or the last definitions that set
it disagree, as two in different files of one module may: files load in the order of the imports,
which is not indexed. An unknown attribute is never reported, only one that no definition sets or a
literal that is wrong. Neither is an entry naming no field or a field of another type, or a related
field with delegate=True, nor a delegate=True field of a model whose _inherits may name it: an
_inherits that is not a literal dict, an _inherit the model reaches that is not a literal, whose
unseen parent may give the entry, or classes in an unknown order that give a parent model different
fields. The order of the entries of two listed models is unknown when different classes of the model
list them, and when the class giving an entry is reached through two listed models. From saas~18.3,
so in 19.0, Odoo refuses every delegate=True field that no _inherits entry names, whatever its
attributes; this rule does not report that.
The rule does not run for a module when the module or one of its dependencies depends on a module
that is not indexed. A model is skipped when the class that introduces it, with _name or as a
class Odoo names, is in a module outside the dependencies, or when a class of it, or of a model it
inherits or delegates to, has a plain Python base class or an _inherit element that is not a
literal, a parent whose fields the scan cannot see. A module that Odoo always installs with base,
such as web, counts as in depends.
Example¶
class ParcelLabel(models.Model):
_name = "parcel.label"
_description = "Parcel label"
_inherits = {"res.partner": "partner_id"}
partner_id = fields.Many2one("res.partner", required=True)
partner_id gives no ondelete: 18.0 forces 'cascade' and logs a warning, and 19.0 refuses
the model.
class ParcelLabel(models.Model):
_name = "parcel.label"
_description = "Parcel label"
_inherits = {"res.partner": "partner_id"}
partner_id = fields.Many2one("res.partner", required=True, ondelete="cascade")