DEV Community

Aaron Jones
Aaron Jones

Posted on

QWeb / View Inheritance Collisions (Odoo)

Problem Statement:
UI breaks or customizations disappear when multiple modules try to extend the same XML view without proper priority or XPath, causing view conflicts or missing UI elements.

Step 1 Understand why the UI breaks
In Odoo, views are layered:

  • Base view (from core module)
  • Inherited views (from multiple modules)
  • Final compiled view (what the user actually sees)

UI breaks when:

  • XPath is too generic
  • Another module changes the structure
  • View priority order is wrong
  • A view replaces large blocks
  • You inherit the wrong parent view

Step 2 Identify the exact conflicting views (mandatory)
In Odoo UI

  • Enable Developer Mode
  • Open the broken form/tree/kanban
  • Click Debug icon
  • Click Edit View: Form / Tree
  • Note:
    • Parent View External ID
    • List of inherited views
    • Which module modifies the same section This tells you who you’re colliding with.

Step 3 Fix the most common mistake: weak XPath
Bad XPath (too generic)

<xpath expr="//group" position="inside"> <field name="x_custom_field"/> </xpath> 
Enter fullscreen mode Exit fullscreen mode

This breaks if any module rearranges groups.

Good XPath (anchor to stable elements)
Target a specific field, page, or group.

<xpath expr="//field[@name='partner_id']" position="after"> <field name="x_custom_field"/> </xpath> 
Enter fullscreen mode Exit fullscreen mode

Or target a page:

<xpath expr="//page[@name='order_lines']" position="inside"> <group> <field name="x_custom_field"/> </group> </xpath> 
Enter fullscreen mode Exit fullscreen mode

Rule: Anchor to something unlikely to change

Step 4 Use priority to control override order
When multiple modules modify the same node, priority decides who wins.

Example: inherited view with priority

<record id="sale_order_form_inherit_custom" model="ir.ui.view"> <field name="name">sale.order.form.inherit.custom</field> <field name="model">sale.order</field> <field name="inherit_id" ref="sale.view_order_form"/> <field name="priority" eval="90"/> <field name="arch" type="xml"> <xpath expr="//field[@name='partner_id']" position="after"> <field name="x_custom_field"/> </xpath> </field> </record> 
Enter fullscreen mode Exit fullscreen mode

Priority rules:

  • Default = 16
  • Higher number → applied later
  • Use 80–99 for override views

Step 5 Never replace large blocks (major collision cause)
Dangerous (overwrites other modules)

<xpath expr="//page[@name='other_info']" position="replace"> <page name="other_info"> ... </page> </xpath> 
Enter fullscreen mode Exit fullscreen mode

This removes all changes from other modules.

Safe alternatives
Modify attributes only

<xpath expr="//field[@name='client_order_ref']" position="attributes"> <attribute name="invisible">1</attribute> </xpath> 
Enter fullscreen mode Exit fullscreen mode

Insert small blocks

<xpath expr="//group[@name='sale_group']" position="inside"> <field name="x_notes"/> </xpath> 
Enter fullscreen mode Exit fullscreen mode

Rule: Insert, don’t replace

Step 6 Inherit the actual modified view (advanced but critical)
If another module heavily modifies the base view, your XPath may not match anymore.

Wrong
<field name="inherit_id" ref="sale.view_order_form"/>

Correct (inherit the modified view)

<field name="inherit_id" ref="module_a.sale_order_form_inherit"/> 
Enter fullscreen mode Exit fullscreen mode

And update __manifest__.py:

'depends': ['sale', 'module_a'],
Now your XPath matches the real structure.

Step 7 Debug the final compiled view (best practice)
In Developer Mode:

  • Edit View
  • Click View
  • Click Inherited Views
  • Use View Architecture If:
  • Your field exists in your XML
  • But not in the compiled architecture

Another view is overriding or removing it.

Step 8 QWeb-specific collisions (reports & templates)
Bad QWeb XPath

<t t-jquery="div" t-operation="append"> <span>Extra</span> </t> 
Enter fullscreen mode Exit fullscreen mode

This may hit multiple nodes.

Good QWeb XPath

<t t-jquery="div.o_invoice_footer" t-operation="append"> <span>Extra Footer Info</span> </t> 
Enter fullscreen mode Exit fullscreen mode

Example QWeb inherit

<template id="report_invoice_inherit_custom" inherit_id="account.report_invoice_document" priority="90"> <xpath expr="//div[@class='page']" position="inside"> <p>Custom Footer</p> </xpath> </template> 
Enter fullscreen mode Exit fullscreen mode

Step 9 Rebuild & test cleanly
After fixing:

./odoo-bin -d your_db -u your_module --dev=xml --log-level=debug
Watch for:

  • Element '<xpath>' cannot be located
  • View validation error Hard refresh browser after upgrade.

Step 10 Collision-proof checklist (use every time)

  • XPath anchored to field/page/group, not //group
  • priority set intentionally
  • No position="replace" unless unavoidable
  • Inherit the actual modified view
  • Test compiled view in Developer Mode
  • Depend on modules you inherit from

Conclusion
QWeb and XML view inheritance collisions happen because multiple modules modify the same UI layer without coordination. Generic XPath expressions, incorrect inheritance targets, and missing priorities cause Odoo to apply views in unexpected ways leading to missing fields, broken layouts, or disappearing customizations. The reliable fix is to use precise XPath selectors, control execution order with view priority, avoid replacing large UI blocks, and always debug the final compiled view. Once these rules are followed, view conflicts become predictable, stable, and easy to maintain even in heavily customized Odoo environments.

Top comments (0)