Skip to content

Odoo glossary

XPath view inheritance

Also called Odoo xpath, Odoo view inheritance, inherit_id, inherited view

Definition

XPath view inheritance is how Odoo changes an existing view without editing it. A new ir.ui.view record points at the original through inherit_id, and xpath elements locate nodes in the parent arch to add content inside, before or after them, replace them, change their attributes or move them.

A form in Odoo is never a single XML file. When the web client asks for the sale order form, the server takes the base view and applies every inheriting view from every installed module on top of it, then sends the result. Your customization is one more layer in that stack.

How the final view gets built

Odoo resolves a view in a fixed order. It starts from the primary view’s arch, then applies the inheriting views whose mode is extension, depth first: a child view, then that child’s own children, then its siblings. When several views inherit the same parent, their priority decides the order.

A view with mode set to primary works differently. It takes the fully resolved parent as its starting point but becomes a separate view, so the original stays untouched for everyone else. That’s the tool when you want a variant of a form for one action rather than a change to the form itself.

Inside a view, each spec finds its target with one of three locators: an xpath element with an expr, a field element matched by name, or any other element matched by tag and identical attributes. Every locator uses the first match only.

The positions

inside is the default and appends to the matched node. after and before insert next to it. replace swaps it out, and a text node containing only $0 inside your spec is replaced by the original node, which lets you wrap an element instead of deleting it. attributes sets or removes attributes. move takes an existing node from elsewhere in the view and puts it where your spec points.

An example

This targets sale.view_order_form, where commitment_date already carries readonly="state == 'cancel' or locked" in 17, 18 and 19:

<record id="view_order_form_delivery_window" model="ir.ui.view">
    <field name="name">sale.order.form.delivery.window</field>
    <field name="model">sale.order</field>
    <field name="inherit_id" ref="sale.view_order_form"/>
    <field name="arch" type="xml">
        <xpath expr="//field[@name='commitment_date']" position="after">
            <field name="delivery_window"/>
            <field name="delivery_window_confirmed"/>
        </xpath>
        <!-- Odoo 18 and 19: combines with the existing expression -->
        <xpath expr="//field[@name='commitment_date']" position="attributes">
            <attribute name="readonly" add="delivery_window_confirmed" separator="or"/>
        </xpath>
        <xpath expr="//field[@name='delivery_window']" position="after">
            <field name="client_order_ref" position="move"/>
        </xpath>
    </field>
</record>

The add with separator="or" is worth knowing. Since Odoo 18, add and remove on invisible, readonly, required, column_invisible and decoration-* treat the value as a Python expression and only accept and or or, producing (state == 'cancel' or locked) or (delivery_window_confirmed). Odoo 17 handles add and remove as plain string splitting on the separator, which mangles an expression, so on 17 you write the whole condition in the attribute body instead.

Version traps

Odoo 17 dropped attrs and states, and an inherited view still using them fails validation. Odoo 18 renamed the root of list views from tree to list, so any xpath like //field[@name='order_line']/tree stops matching. Both errors are loud on install, which is better than silent. So is anchoring on a label: //field[@string='Customer'] is rejected outright, because string is translated and Odoo won’t use translated attributes as selectors. Matching on @class only logs a warning, and the fix it suggests is hasclass().

The quieter problem is two addons that don’t depend on each other targeting the same node. Priority settles the order, but if one of them replaces or moves that node, the other can fail with “cannot be located in parent view” on a database where both are installed. Our post on _inherit vs _inherits lists more of the xpath mistakes we catch in review.

erpfly writes view changes this way, anchored on field names, and the Odoo customization page shows a larger inherited form. PDF reports use the same specs, since a report is a QWeb template stored as a view.

Sources

The official documentation and source code this page was checked against.

  1. 01 View records: Inheritance, Odoo 19 developer documentation odoo.com
  2. 02 View architectures, Odoo 19 developer documentation odoo.com
  3. 03 odoo/tools/template_inheritance.py (18.0), odoo/odoo on GitHub github.com

Where XPath view inheritance comes up

Terms used on this page

More Odoo terms

Your next module is one paragraph away

Write it the way you’d explain it to a new hire. We’ll turn it into an app you can read, test and install.

Odoo 19

Create your account

Free to start. No card needed.

By signing up you agree to our terms and privacy policy.