OD220 duplicate-field-label¶
Two fields of a model have the same label.
| Category | Severity | Series | Needs the index |
|---|---|---|---|
| Correctness | Warning | all | yes |
What it reports¶
A field whose label another field of the same model already has. The label is the literal string,
by keyword or by position, or for a field that gives none and is not related, the one Odoo derives
from its name: without a trailing _ids or _id, underscores made spaces, and each word
capitalized, so partner_id and partner_ids are both Partner. The fields Odoo adds to a model
count with their labels: ID and Display Name, Last Modified on (__last_update) until 16.0,
and on a model that logs access, Created by, Created on, Last Updated by and
Last Updated on. An abstract model has the first three until saas~14.3, so in 12.0 to 14.0,
none of them from saas~14.4 to 18.0, so in 15.0 to 18.0, and ID and Display Name from
saas~18.1, so from 19.0, where BaseModel defines them. A module of unknown series is checked as
one of saas~14.4 to 18.0, without Last Modified on.
The finding is at the definition in the module that decides the label: the last one that sets
string, or the field's first definition when the label is derived. A label is reported against
one that another module decides, or one that a definition this module loads earlier decides, so
each pair is reported once. When the two definitions are in different files of the module, the
order of the files in the module picks the one reported, since the order of the imports is not
indexed.
Why it matters¶
When Odoo records a model's fields in ir.model.fields, it logs a warning for each field whose
label another field of the model already has. The label is what users see as the field's column,
filter and export name, so two fields with one label cannot be told apart. Give one of them another
string. Before saas~11.2, Odoo logs no such warning, and the rule stays silent on such a series.
A module of unknown series is checked all the same, so one on such a series gets a false report:
pass --odoo-version.
A label that is not a literal, such as _lt("Notes"), is unknown and never reported, nor is a
related field without string, which takes the label of the field it relates to, nor a field the
model gets through _inherits. The fields that log access are counted unless the model is
abstract, or a class of it sets _auto or _log_access to anything but True.
What a class that redefines one of the fields Odoo adds makes of its label depends on the series.
From saas~14.4, so from 15.0, Odoo adds the field to the class that introduces the model with
_name, which merges after the models it inherits, so a string that a mixin sets gives way to the
label Odoo gives the field, which stands unless a later class of the model sets string. Odoo does
not add a field of a name the introducing class defines, so the definitions decide the label, a
mixin's string included when that class sets none. Two fields are exceptions: in 15.0 to 18.0 Odoo
adds id even over the introducing class's definition, and from saas~18.1, so from 19.0,
BaseModel defines id and display_name before every class. The field Odoo adds as id sets no
string, so for id, and for display_name from 19.0, the string of any definition stands, a
mixin's included. For any other field, when a class of the model lists another model before the
model's own in _inherit, that model merges after the field Odoo adds, so the label of a field that
a model the model inherits defines is unknown and never reported.
Until saas~14.3, so in 12.0 to 14.0, Odoo adds no field that a class of the model or of a model it
inherits defines, so the definitions decide the label, except for id, which keeps its label
whatever a class sets. Until saas~12.4, so in 12.0, a definition without string also takes the
label of the field Odoo added to a model the model inherits: Display Name always, and the label
of a field that logs access when that model logs access; when a model the model inherits defines
such a field too, its label is unknown and never reported. Last Modified on always stands: Odoo
adds __last_update whatever a class defines, and Python renames the name in a class body (to _,
the class name without its leading underscores, then the name), so no class redefines it.
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 ParcelBag(models.Model):
_name = "parcel.bag"
_description = "Parcel bag"
partner_id = fields.Many2one("res.partner")
partner_ids = fields.Many2many("res.partner")
class ParcelBag(models.Model):
_name = "parcel.bag"
_description = "Parcel bag"
partner_id = fields.Many2one("res.partner")
partner_ids = fields.Many2many("res.partner", string="Followers")