Articles > Odoo 15 Manifest Asset Bundles

Odoo 15 Manifest Asset Bundles

Written by
Holden Rehg
Posted on
October 7, 2021 at 8:12 AM

In the last few weeks I have migrated over 30 modules from version 14 to version 15 of Odoo. This seems to be part of our painstaking yearly ritual as Odoo developers to attempt to keep our projects up to date with the latest annual release.

As some people may know, I believe there are better options as a community than to do annual, non-backwards compatible releases without automated tooling provided as an upgrade path. I've talked about that a bit in my managing hundreds of local development projects article.

But this year specifically, I have less to complain about at least. Out of 30+ modules migrated, across a spectrum of modules/domains, there were very few changes between 14 and 15. I'm really hoping that other developers have the same experience as they go through this process.

The two biggest changes I've seen

I ran into 2 big changes that affect us developers day to day.

1. OWL

This is a big change. For anyone who hasn't heard, Odoo built a new frontend JavaScript framework called OWL. Luckily, for anyone not interested in migrated all of their old JS code, it appears the old frontend system has not been entirely scrapped (yet). All of the JavaScript that I migrated still worked properly. Over time, it's going to make a lot more sense to use OWL though.

2. Asset bundling

The second is asset bundling. This is what I wanted to talk about with this article.

Odoo creates "asset bundles" that get loaded into the frontend of the application. Each bundle contains different sets of assets. The assets are either JavaScript or CSS. As developers, we need a way to hook into these bundles and say "put my custom JS or CSS code into that bundle".

Useful bundles

There is a set of common bundles to inherit. The majority of custom code written will fit into these bundles:

  • web.assets_common: Loaded everywhere (frontend, backend, point of sale).
  • web.assets_backend: Only loaded into the "backend" of the application. By backend, I'm talking about where you login as a backend user at /web/login. This bundle is excluded from the frontend website.
  • web.assets_frontend: The opposite of web.assets_backend, only loaded into the frontend website.
  • web.assets_qweb: Loads QWeb XML files.
  • web.assets_wysiwyg: Loads assets into the backend wysiwyg text editor.
  • website.assets_editor: Loads assets into the frontend website edit mode.
  • web.assets_tests: Loads assets for frontend testing and tours.
  • web._assets_primary_variables: Used to load custom scss variables for styles.

There are of course other bundles spread out throughout Odoo core. Some are used as helpers or aliases, while others are specific to certain modules (non-web modules). I wouldn't worry about the other bundles too much unless you are working on a specific or more obscure use case.

The old way

Prior to version 15, when we wanted to add any custom JS or CSS to our modules, we would add it by inheriting a QWeb template:

# my_module/assets.xml
<odoo>
    <template id="assets_backend" inherit_id="web.assets_backend">
        <xpath expr="." position="inside">
            <script .../>
            <link .../>
        </xpath>
    </template>
</odoo>
    

Typically this means having some sort of file called assets.xml in the module and then adding a reference to the __manifest__.py:

# my_module/__manifest__.py
{
    ...

    "data": [
        "assets.xml"
    ]
}
    
The new way

In version 15, there is no more template override. You do not need to create an assets.xml file, with a <template/> tag to inherit.

Now, it's all done in the __manifest__.py files. For example, if we create a JS file that we want to load into our module then we can add it to a bundle by defining an assets key in our __manifest__.py dictionary. Within assets we will add the name of the bundle that we want to load into, and then a list of file paths starting your module name. For example, a custom script path may look like {module_name}/path/to/script.js:

/** @odoo-module **/

export function hello() {
    console.log("hello, from our new script");
}
    
# my_module/__manifest__.py
{
    ...

    "assets": {
        "web.assets_backend": [
            "my_module/static/src/js/my_script.js"
        ]
    }
}
    

It's one less step than the old way of doing things.

A note on flexibility

This new system is slightly less flexible at first glance. Before, we could control exactly where we were inserting assets based on an xpath. Now, by default it's going to append the assets to the very end of the bundle.

Odoo introduced some ways around that. You can include "operations" when loading in assets to control where to place assets inside of the bundle.

append

Appends to the end of the bundle:

{
    "assets": {
        "web.assets_backend": [
            "my_module/path/to/file"
        ]
    }
}
    

prepend

Prepends to the beginning of the bundle:

{
    "assets": {
        "web.assets_backend": [
            ("prepend", "my_module/path/to/file")
        ]
    }
}
    

before

Place your file before another file in the bundle:

{
    "assets": {
        "web.assets_backend": [
            (
                 "before",
                 # looks for this asset in
                 # the parent bundle...
                 "web/path/to/target",
                 # and places this asset before it.
                 "my_module/path/to/file",
            )
        ]
    }
}
    

after

Place your file after another file in the bundle:

{
    "assets": {
        "web.assets_backend": [
            (
                 "after",
                 # looks for this asset in
                 # the parent bundle...
                 "web/path/to/target",
                 # and places this asset after it.
                 "my_module/path/to/file",
            )
        ]
    }
}
    

include,remove,replace

There are additional, less common operation options. The official documentation has more, detailed information about these if you need them. I'd recommend reading through it just in case you run into a scenario where you need more fine grained control of asset bundling.

Final thoughts

To be honest, I'm conflicted on this update. In general I see why they made the decision. It removed an extra step for developers.

At the same time, it feels like another "system" that we need to understand. There's implicit "magic" happening in the manifest files with the new assets option. It isn't entirely obvious to new developers. As Odoo developers, we have to learn QWeb no matter what. So before, after learning QWeb, it was clear what was happening when inheriting the web.assets_backend template. We inherited it, wrote an xpath looking to the parent template, inserted some line of code for script or link which was explicit, and then we knew that it funneled its way into the <head/> of the final HTML at some point.

Now there is one additional layer to understand. One additional conceptual barrier to understanding how the underlying system works. For developers who have spent a lot of time with Odoo like myself, this is a benefit, but from the perspective of a brand new developer, I think it's one more thing that needs to be understood in an already large, complex system.

Thanks For Reading

I appreciate you taking the time to read any of my articles. I hope it has helped you out in some way. If you're looking for more ramblings, take a look at theentire catalog of articles I've written. Give me a follow on Twitter or Github to see what else I've got going on. Feel free to reach out if you want to talk!

python
odoo
Share:

Holden Rehg, Author

Posted October 7, 2021 at 8:12 AM