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 ofweb.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!