Skip to content

OD310 unmatched-view-locator

An inherited view locates an element its parent views do not have.

Category Severity Series Needs the index
Correctness Error all yes

What it reports

A spec in an inherited view, such as <field name="x" position="after"> or an <xpath>, that names an element by its name, id or t-call when no such element is in the view it extends, in that view's own parents, or in the other views of the dependency graph that extend them. Each named step of an xpath, such as group[@name='x'] or t[@t-call='x'], must match. A spec that finds the element only in a view added by a module missing from depends is reported too. A module that Odoo always installs with base, such as web, counts as in depends.

Place makes a spec, not position, which defaults to inside. A spec is the root of an inherited arch, each child of a <data> root, or each child of a <template> with a non-empty inherit_id. What a spec holds is content, and so is what a view without a parent holds, whatever position it carries: from 19.0 on that is a parameter of a t-call. Only a position="move" child of a spec locates. A <template t-call="x"> without a parent has the element <t t-call="x"> as its root. A record whose inherit_id is eval="False", an empty ref or no value at all has no parent, so its arch holds elements and no spec.

Only a file Odoo loads gives a parent or an extension: one the manifest lists under a key the module's series reads, as OD502 tabulates, or one the module loads itself. A file whose name appears in the module's Python code counts, whatever key lists it, as that code loads it (18.0 addons/hr/models/hr_employee.py:696). So does a chart template under data/template/, on every series though Odoo reads one from saas~16.2. A file listed under a key Odoo loads nothing from, such as other_files, counts unless a legacy key the series ignores lists it too, as it was meant to load from there. 18.0 reads data and not update_xml (odoo/modules/loading.py:39-48), so a file under update_xml and other_files counts as unlisted there, while 9.0 (openerp/modules/loading.py:60-64) and 10.0 (odoo/modules/loading.py:57-61) load it. An element that only an extension in any other file adds is reported: list that file under data.

Why it matters

Odoo applies the specs when it builds the view, and a spec that matches nothing stops the install with Element '<field name="x">' cannot be located in parent view. A demo file differs: see load failures. When the element comes from a module outside depends, the view builds where that module is installed and breaks where it is not. The cause is usually a typo, or an element that the parent module renamed or removed in a new series, which a port carries over unnoticed until the install.

The rule only checks what it can match exactly. An xpath with | or or, a step without a name, id or t-call, a spec on a parent view outside depends, which is OD302's to report, and a spec on a parent found only in a file Odoo does not load, are left alone. A field locator matches a field anywhere in the parent, subviews included, as it does in Odoo.

The rule stays silent on a tree of views that a dependency that is not indexed, such as an Enterprise module, could add the element to. It still reports a tree whose root the module defines, as on a model the module defines whole, since that dependency loads first and cannot extend it. Add the checkout of the dependency that is not indexed to --addons-path to check the other trees.

Example

views/sale_order_views.xml
<field name="arch" type="xml">
    <field name="payment_terms_id" position="after">
        <field name="client_order_ref"/>
    </field>
    <xpath expr="//group[@name='sale_heading']" position="inside">
        <field name="commitment_date"/>
    </xpath>
</field>

The sales order form has a payment_term_id field and a sale_header group, so neither spec finds its element.

views/sale_order_views.xml
<field name="arch" type="xml">
    <field name="payment_term_id" position="after">
        <field name="client_order_ref"/>
    </field>
    <xpath expr="//group[@name='sale_header']" position="inside">
        <field name="commitment_date"/>
    </xpath>
</field>

Suppressing

<!-- odooscan: ignore[OD310] -->

See suppressing a finding.