OD213 required-many2one-set-null¶
A required Many2one declares ondelete='set null'.
| Category | Severity | Series | Needs the index |
|---|---|---|---|
| Correctness | Error | 13.0 and later | yes |
What it reports¶
A Many2one that is required=True and ondelete='set null', whether one definition gives both
or they come from different definitions of the field in the module and its dependencies. A module
that Odoo always installs with base, such as web, counts as in depends. The field is reported
once per module, at its last definition there that sets either, on ondelete when that definition
sets it. Related fields are not checked. Modules of earlier series are not checked.
From saas~14.4 to saas~18.2, so in 15.0 to 18.0, a field that may delegate is not reported: one that
sets delegate to anything but a literal Python takes as false, such as False, None or 0, or
one that an entry of its model's _inherits names. A model merges the _inherits of its classes
and of the models it inherits key by key, a later class replacing the field an earlier one gives a
parent model; 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 field that only a later listed model's
entry for the same parent model names does not delegate and is reported. Odoo replaces the field's
ondelete with cascade before it compares it with required, so the combination does not fail.
OD218 reports such a field at the entry of its model that names it, or when it has delegate=True
and no entry names it. Neither rule reports one that only an entry of an inherited model names, one
whose entry sits in a module other than the one scanned (a module that sets ondelete on
res.users's partner_id, which base's entry names), or whose delegate is neither True nor a
false literal. When an _inherits is not a literal dict, an _inherit the model reaches is not a
literal, whose unseen parent may give an entry, or classes in an unknown order give a parent model
different fields, which fields the _inherits names is unknown, and those it may name are not
reported. 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. Until
saas~14.3, so in 13.0 and 14.0, Odoo compares ondelete with required before the replacement and
fails, and from saas~18.3, so in 19.0, it refuses a delegating field with set null: OD213 reports
the field in both. From saas~18.3, OD218 reports it only at an entry of its own model.
Why it matters¶
set null empties the field when the record it points to is deleted, which a required field
cannot be. Odoo refuses the combination when it sets up the model, and the error stops the
registry from loading: the module cannot be installed, and a database that has it does not start.
Use restrict to forbid deleting a record still in use, or cascade to delete the records that
point to it with it. A required Many2one that gives no ondelete gets restrict, or cascade
when its model is transient and the comodel is not.
An attribute takes its value from the last definitions that set it, and is unknown when those
disagree, as two definitions in different files of one module may: files load in the order of the
imports, which is not indexed. When required or ondelete is unknown, or a definition unpacks
**kwargs, the field is not reported, nor is a field whose definitions change its type in an
unknown order.
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.
Example¶
class FleetVehicle(models.Model):
_inherit = "fleet.vehicle"
depot_id = fields.Many2one("stock.warehouse", required=True, ondelete="set null")
class FleetVehicle(models.Model):
_inherit = "fleet.vehicle"
depot_id = fields.Many2one("stock.warehouse", required=True, ondelete="restrict")