OD210 unknown-domain-field¶
A literal domain or group_by names a field its model does not have.
| Category | Severity | Series | Needs the index |
|---|---|---|---|
| Correctness | Error | all | yes |
What it reports¶
A field path in a domain written as a literal, or in a group_by, that does not resolve on the
model the domain filters. Paths are followed as OD203 follows them. The domains read
are those of:
- a relational field's
domain, on its comodel; search,search_count,search_read,search_fetch,filtered_domain,read_groupand their private forms, called onselfor onenv["x.y"], on that model;- a window action's
domainand thegroup_byof itscontext, on itsres_model; - a view's
domainandfilter_domainattributes and thegroup_byof a filter'scontext, on the model at that place in the arch; - an
ir.rule'sdomain_force, and anir.access'sdomain, written by a<record>or a CSV row, on itsmodel_id.
Why it matters¶
Odoo checks a domain's fields only when it uses the domain. A search in Python raises ValueError
when the line runs, a field's domain fails when a user opens its dropdown, and an action fails
when it opens. From saas~13.1 a domain or group_by in a view arch, filter_domain aside, is
checked when the view is validated, so the install fails. Fields renamed or removed between versions
are the usual cause after a migration.
From 15.0 Odoo validates a record rule's domain each time it writes one, so a wrong field fails the
install with a ValidationError, even when a later file replaces the domain. A demo file differs,
here and above: see load failures.
Before 15.0 nothing checks it until a query applies the rule, which then fails for the users it
covers, so only the domain left after the whole load counts. A demo write does not count for a data
value, as a database may hold no demo data.
A domain the code builds at runtime is skipped as a whole: a term whose field is not a string
literal, a domain added to another, or one unpacked from a variable. A term such as (1, "=", 1)
names no field. Searches in code that runs only once a check at runtime has shown that a model, a
field or a module is there, as OD206 describes, and searches on self in an abstract
model are not checked. A granularity such as date:month is dropped from a group_by before the
field is looked up.
A field of a soft dependency is reported as out of depends unless such a check reaches the search:
add the module to depends, or test for it around the search.
A field of another module that Odoo auto-installs with the module is not reported in a domain that
runs once the install is done, as the field is there by then. Another module counts when every
depend it waits on lies in the module's depends closure, with what Odoo always installs, or among
the modules already counted, repeating until nothing is added. The depends it does not wait on come
with it. auto_install: True, and a list before saas~12.4, waits on every depend. From saas~17.1 a
module with a countries list does not count, as whether it installs rests on the companies of the
database, so its field stays reported: add it to depends. An auto-installed module whose
version names no series takes the series of the module being checked, as the two install on one
Odoo. When neither names one, the countries list counts, so a field read on a series before
saas~17.1 gets a false report: pass --odoo-version.
A domain Odoo checks while the module loads needs the field earlier, so an auto-installed module
counts there only when it loads first: less deep in the depends graph, or as deep with a name that
sorts first. These are a domain or group_by in a view from saas~13.1, a relational field's string
domain from the same series, as a view that lets a user edit the field checks it, and a record
rule from 15.0. A field of a module that loads after is reported, as a database that installs
the two together refuses the module: add the other one to depends. A filter_domain and a field's
list domain wait for a user on every series, as does any domain before those series.
The rule stays silent on a model that a dependency that is not indexed, such as an Enterprise
module, could add the field to. It still reports a name that fails on a model the module defines
whole, with every class and parent in the module, since that dependency loads first and cannot
extend it. It also still reports a path that continues past a field that is not relational. Add the
checkout of the dependency that is not indexed to --addons-path to check the other models.
A model may hold a field in a parent the scan cannot read, so a field missing from a model is not
reported 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. Odoo gives a model the fields of a model class
used as a base on every series, while a class that is no model holds fields only before saas~14.4,
and the scan cannot always tell the two apart. A base defined above the model in the same file is
read when the model also inherits or delegates to it, unless it is a class of base, so to keep
such a model under the check, define the parent there and name it in _inherit as well. A plain
class the file defines above the model, which the scan can read as holding no field, hides nothing,
so a typo on that model is reported. A path is judged at the model where it fails, not the one it
starts from: a typo past a relation is reported when the comodel hides no parent, whatever the model
of the domain. A field the model has but depends does not reach, and a path that continues past a
field that is not relational, are reported whatever the parents. A class in scope in a
file that does not parse counts as one the scan cannot read, so such a field is left
alone on its model.
A field missing from a model is not reported either when the model, or one it inherits or delegates
to, has a parent no indexed module defines or delegates to a parent the scan cannot read, or when a
module in scope loads an unread data file, which may add a field to any model. One of a
dependency loads first, so it cannot add to a model the module defines whole. A model the module
builds at runtime, as OD202 describes, is skipped too, for a field missing from it and
for one depends does not reach, unless a class in scope introduces it with _name. Put the module
that defines the parent on the addons path to keep it under the check.
A record rule is checked on the last model_id written to its id, in a ref, an eval that is one
ref(), a search of one ('model', '=', name) term, a :id or /id column, or the plain name
of an ir.access CSV. It is skipped when that value takes a run to tell, when the record is nested
in a one2many, or when its last active is false or an expression. A domain Odoo reads through
ref, search, file or type, a <function> that writes one, and a domain with %(id)d are not
read. A scope with a dependency that is not indexed, a module that names models at runtime, a model
outside depends and a model with an _inherit no module defines skip the check too.
A field is what Odoo takes as one: a class attribute that holds an instance of a field class. The
scan knows such a class by its own name, across the whole scan. The name is that of a public class
of Odoo's fields module, or of a class of a scanned module that derives from one through fields,
directly or through other classes of the scan. A class of another module that shares the name
counts too, and when two such classes differ in type, a path stops at the field and its type is not
judged. A class renamed on import is not known by that name, and neither is one that derives from a
private class of fields or from another module of the ORM. A class the scan does not know stays
unknown, so a domain on an attribute built from it is reported: put the module that defines the
class on the addons path, use the class under its own name, and derive it from a public class of
fields.
A field an ir.model.fields record adds counts in every domain, as OD304 describes, and
OD203 tells how a path continues through it.
A subview an extension adds inside a field of the parent view is read on that field's comodel. A spec locates the first field of that name anywhere, so such a subview is skipped when a subview of the inheritance tree is on a model that may have that field too.
Example¶
class SaleOrder(models.Model):
_inherit = "sale.order"
referrer_id = fields.Many2one("res.partner", domain=[("customer", "=", True)])
def _late_orders(self):
return self.search([("state", "=", "sale"), ("commitment_dat", "<", fields.Datetime.now())])
res.partner has had no customer field since 13.0, and commitment_dat is a typo.
class SaleOrder(models.Model):
_inherit = "sale.order"
referrer_id = fields.Many2one("res.partner", domain=[("customer_rank", ">", 0)])
def _late_orders(self):
return self.search([("state", "=", "sale"), ("commitment_date", "<", fields.Datetime.now())])
A record rule fails the same way:
<record id="sale_order_own" model="ir.rule">
<field name="model_id" ref="sale.model_sale_order"/>
<field name="domain_force">[('salesman_id', '=', user.id)]</field>
</record>
sale.order has no salesman_id, so 15.0 and later refuse to install the module.
<record id="sale_order_own" model="ir.rule">
<field name="model_id" ref="sale.model_sale_order"/>
<field name="domain_force">[('user_id', '=', user.id)]</field>
</record>