DEV Community

Cover image for Don't break your (and others) Odoo XML
Apruzzese Francesco
Apruzzese Francesco

Posted on • Edited on

Don't break your (and others) Odoo XML

Every time I have to install third-party modules in Odoo, I start praying for 12 hours in advance.

It is not uncommon, in fact, that the installation of some modules leads to the breaking of pre-existing views and, in the presence of already customized views, the risk becomes even greater.

The more view inheritance is nested, the more likely it is that a programmer in the world will die.

I'll list a series of simple rules to follow to avoid releasing code that could kill some installations and that doesn't make us bring on our conscience the lives of our colleagues distributed around the world.

Replace

Replace is like a bazooka. It destroys everything. That means if someone after us tries to find what we replaced, they'll never find it. It is not difficult to understand that replace should be used sparingly and that, if possible, it should not be used at all. It is rare that the only solution to our problem is to replace a node. It is much more elegant to hide it with due care and with much more elegance.

Add attributes

Let's imagine that we have a div, with its own class, to which we want to add a new one. What often happens is that we use the "replace" to replace the node with the same node to which we add our class. This is wrong for two reasons: the original node is no longer recognizable from the previous xpath and two modules that apply the same modification tend to obscure each other.

The best thing to do is to use the addition of value through the use of attributes.

Wrong:



<xpath expr="//div[@id='node_id']" position="replace">
    <div id="node_id" class="row d-none" />
</xpath>


Enter fullscreen mode Exit fullscreen mode

Right:



<xpath expr="//div[@id='node_id']" position="attributes">
    <attribute name="class" separator=" " add="d-none" />
</xpath>


Enter fullscreen mode Exit fullscreen mode

You can use it to extend if condition in view, too.

Example:



<xpath expr="//button[@id='o_payment_form_pay']" position="attributes">
    <attribute name="t-if" separator=" " add="and accept_terms_conditions" />
</xpath>


Enter fullscreen mode Exit fullscreen mode

Hasclass

As we have seen previously, a node can be extended by adding a new class. This highlights another problem. Very often we refer to nodes by indicating their class.

Example:



<xpath expr="//div[@class='myclass']" position="...">
    ...
</xpath>


Enter fullscreen mode Exit fullscreen mode

If a module, before ours, adds a new class to the node we are looking for, we may never find it again. In this case, the solution is given by the hasclass function that allows us to control the nodes that have the class we are looking for among all those available.

Example:



<xpath expr="//div[hasclass('myclass')]" position="...">
    ...
</xpath>


Enter fullscreen mode Exit fullscreen mode

ID and Name abuse (Do it!)

When using xpath, you always try to hook into a node that is uniquely recognizable throughout the entire XML structure. This is not always so easy because some nodes are very anonymous (they have too generic class, they are the same as other nodes in other places, they are repeated in the structure).

In this case, it is a good practice to use the id or name attribute on nodes that we believe can be hooked so as to facilitate the work of developers who will extend our views.

Do not obscure

When the starting structure is very complex and there are many changes to be made, there may be the temptation to obscure a view by creating a new one with the same XML ID. It is useless to explain how dangerous and harmful this practice is. The XML ID is the parameter used to inherit views and forms that come after yours would expect a structure that is actually completely disrupted.

If there are so many changes to make, you're probably not using the correct view and the best way is to create a new one and convince the software to use yours instead of the original one.

Variables, not hardcore

Whenever you need a value to perform operations (compare values, numbers of cycles for a loop, etc.) it would be convenient to use variables so that the behavior in the operations can be more easily modified.

Wrong:



<ul>
    <t t-foreach="['a', 'b', 'c']" t-as="val">
        <li><span t-esc="val" /></li>
    </t>
</ul>


Enter fullscreen mode Exit fullscreen mode

Right:



<t t-set="vals_to_loop" t-value="['a', 'b', 'c']"/>
<ul>
<t t-foreach="vals_to_loop" t-as="val">
<li><span t-esc="val" /></li>
</t>
</ul>

Enter fullscreen mode Exit fullscreen mode




Final considerations

Unfortunately, this list is only part of the good practice to keep in mind. No code is perfect, and in many cases, even Odoo's core code is not free from these problems.

I only hope that these rules can serve as a springboard for writing better code.

Support

Buy Me A Coffee

Top comments (3)

Collapse
 
musula profile image
Waiyaki

@opencode
Thanks. How can I add a field to a template (base.view_partner_form) that has already been inherited by template_x using position="replace"?
Trying to change to template_x to position="attributes" results in lose of the changes contained within template_x

Collapse
 
opencode profile image
Apruzzese Francesco

You have to inherit the view that change the position and use the xpath to recognize the field you're searching for

Collapse
 
musula profile image
Waiyaki

Thanks for this, very helpful.