QWeb is the primary templating engine used by Odoo[2]. It is an XML templating engine[1] and used mostly to generate HTML fragments and pages.
Template directives are specified as XML attributes prefixed with t-, for instance t-if for conditionals, with elements and other attributes being rendered directly.
To avoid element rendering, a placeholder element <t> is also available, which executes its directive but doesn’t generate any output in and of itself:
<t t-if="condition">
<p>Test</p>
</t>
will result in:
<p>Test</p>
if condition is true, but:
<div t-if="condition">
<p>Test</p>
</div>
will result in:
<div>
<p>Test</p>
</div>
QWeb has a primary output directive which automatically HTML-escape its content limiting XSS risks when displaying user-provided content: esc.
esc takes an expression, evaluates it and prints the content:
<p><t t-esc="value"/></p>
rendered with the value value set to 42 yields:
<p>42</p>
There is one other output directive raw which behaves the same as respectively esc but does not HTML-escape its output. It can be useful to display separately constructed markup (e.g. from functions) or already sanitized user-provided markup.
QWeb has a conditional directive if, which evaluates an expression given as attribute value:
<div>
<t t-if="condition">
<p>ok</p>
</t>
</div>
The element is rendered if the condition is true:
<div>
<p>ok</p>
</div>
but if the condition is false it is removed from the result:
<div>
</div>
The conditional rendering applies to the bearer of the directive, which does not have to be <t>:
<div>
<p t-if="condition">ok</p>
</div>
will give the same results as the previous example.
QWeb has an iteration directive foreach which take an expression returning the collection to iterate on, and a second parameter t-as providing the name to use for the “current item” of the iteration:
<t t-foreach="[1, 2, 3]" t-as="i">
<p><t t-esc="i"/></p>
</t>
will be rendered as:
<p>1</p>
<p>2</p>
<p>3</p>
Like conditions, foreach applies to the element bearing the directive’s attribute, and
<p t-foreach="[1, 2, 3]" t-as="i">
<t t-esc="i"/>
</p>
is equivalent to the previous example.
foreach can iterate on an array (the current item will be the current value), a mapping (the current item will be the current key) or an integer (equivalent to iterating on an array between 0 inclusive and the provided integer exclusive).
In addition to the name passed via t-as, foreach provides a few other variables for various data points:
Warning
$as will be replaced by the name passed to t-as
These extra variables provided and all new variables created into the foreach are only available in the scope of the``foreach``. If the variable exists outside the context of the foreach, the value is copied at the end of the foreach into the global context.
<t t-set="existing_variable" t-value="False"/>
<!-- existing_variable now False -->
<p t-foreach="[1, 2, 3]" t-as="i">
<t t-set="existing_variable" t-value="True"/>
<t t-set="new_variable" t-value="True"/>
<!-- existing_variable and new_variable now True -->
</p>
<!-- existing_variable always True -->
<!-- new_variable undefined -->
QWeb can compute attributes on-the-fly and set the result of the computation on the output node. This is done via the t-att (attribute) directive which exists in 3 different forms:
an attribute called $name is created, the attribute value is evaluated and the result is set as the attribute’s value:
<div t-att-a="42"/>
will be rendered as:
<div a="42"></div>
same as previous, but the parameter is a format string instead of just an expression, often useful to mix literal and non-literal string (e.g. classes):
<t t-foreach="[1, 2, 3]" t-as="item">
<li t-attf-class="row {{ item_parity }}"><t t-esc="item"/></li>
</t>
will be rendered as:
<li class="row even">1</li>
<li class="row odd">2</li>
<li class="row even">3</li>
if the parameter is a mapping, each (key, value) pair generates a new attribute and its value:
<div t-att="{'a': 1, 'b': 2}"/>
will be rendered as:
<div a="1" b="2"></div>
if the parameter is a pair (tuple or array of 2 element), the first item of the pair is the name of the attribute and the second item is the value:
<div t-att="['a', 'b']"/>
will be rendered as:
<div a="b"></div>
QWeb allows creating variables from within the template, to memoize a computation (to use it multiple times), give a piece of data a clearer name, ...
This is done via the set directive, which takes the name of the variable to create. The value to set can be provided in two ways:
a t-value attribute containing an expression, and the result of its evaluation will be set:
<t t-set="foo" t-value="2 + 1"/>
<t t-esc="foo"/>
will print 3
if there is no t-value attribute, the node’s body is rendered and set as the variable’s value:
<t t-set="foo">
<li>ok</li>
</t>
<t t-esc="foo"/>
will generate <li>ok</li> (the content is escaped as we used the esc directive)
Note
using the result of this operation is a significant use-case for the raw directive.
QWeb templates can be used for top-level rendering, but they can also be used from within another template (to avoid duplication or give names to parts of templates) using the t-call directive:
<t t-call="other-template"/>
This calls the named template with the execution context of the parent, if other_template is defined as:
<p><t t-value="var"/></p>
the call above will be rendered as <p/> (no content), but:
<t t-set="var" t-value="1"/>
<t t-call="other-template"/>
will be rendered as <p>1</p>.
However this has the problem of being visible from outside the t-call. Alternatively, content set in the body of the call directive will be evaluated before calling the sub-template, and can alter a local context:
<t t-call="other-template">
<t t-set="var" t-value="1"/>
</t>
<!-- "var" does not exist here -->
The body of the call directive can be arbitrarily complex (not just set directives), and its rendered form will be available within the called template as a magical 0 variable:
<div>
This template was called with content:
<t t-raw="0"/>
</div>
being called thus:
<t t-call="other-template">
<em>content</em>
</t>
will result in:
<div>
This template was called with content:
<em>content</em>
</div>
The t-field directive can only be used when performing field access (a.b) on a “smart” record (result of the browse method). It is able to automatically format based on field type, and is integrated in the website’s rich text edition.
t-field-options can be used to customize fields, the most common option is widget, other options are field- or widget-dependent.
invokes a debugger using PDB’s set_trace API. The parameter should be the name of a module, on which a set_trace method is called:
<t t-debug="pdb"/>
is equivalent to importlib.import_module("pdb").set_trace()
Most Python-side uses of QWeb are in controllers (and during HTTP requests), in which case templates stored in the database (as views) can be trivially rendered by calling openerp.http.HttpRequest.render():
response = http.request.render('my-template', {
'context_value': 42
})
This automatically creates a Response object which can be returned from the controller (or further customized to suit).
At a deeper level than the previous helper is the render method on ir.ui.view:
Renders a QWeb view/template by database id or external id. Templates are automatically loaded from ir.ui.view records.
Sets up a number of default values in the rendering context:
Parameters: |
|
---|
It is also possible to use the ir.qweb model directly (and extend it, and inherit from it):
The t-name directive can only be placed at the top-level of a template file (direct children to the document root):
<templates>
<t t-name="template-name">
<!-- template code -->
</t>
</templates>
It takes no other parameter, but can be used with a <t> element or any other. With a <t> element, the <t> should have a single child.
The template name is an arbitrary string, although when multiple templates are related (e.g. called sub-templates) it is customary to use dot-separated names to indicate hierarchical relationships.
Template inheritance is used to alter existing templates in-place, e.g. to add information to templates created by an other modules.
Template inheritance is performed via the t-extend directive which takes the name of the template to alter as parameter.
The alteration is then performed with any number of t-jquery sub-directives:
<t t-extend="base.template">
<t t-jquery="ul" t-operation="append">
<li>new element</li>
</t>
</t>
The t-jquery directives takes a CSS selector. This selector is used on the extended template to select context nodes to which the specified t-operation is applied:
if no t-operation is specified, the template body is interpreted as javascript code and executed with the context node as this
Warning
while much more powerful than other operations, this mode is also much harder to debug and maintain, it is recommended to avoid it
The javascript QWeb implementation provides a few debugging hooks:
takes an expression parameter, evaluates the expression during rendering and logs its result with console.log:
<t t-set="foo" t-value="42"/>
<t t-log="foo"/>
will print 42 to the console
triggers a debugger breakpoint during template rendering:
<t t-if="a_test">
<t t-debug="">
</t>
will stop execution if debugging is active (exact condition depend on the browser and its development tools)
the node’s body is javascript code executed during template rendering. Takes a context parameter, which is the name under which the rendering context will be available in the t-js‘s body:
<t t-set="foo" t-value="42"/>
<t t-js="ctx">
console.log("Foo is", ctx.foo);
</t>
(core is the web.core module) An instance of QWeb2.Engine() with all module-defined template files loaded, and references to standard helper objects _ (underscore), _t (translation function) and JSON.
core.qweb.render can be used to easily render basic module templates
The QWeb “renderer”, handles most of QWeb’s logic (loading, parsing, compiling and rendering templates).
OpenERP Web instantiates one for the user in the core module, and exports it to core.qweb. It also loads all the template files of the various modules into that QWeb instance.
A QWeb2.Engine() also serves as a “template namespace”.
Renders a previously loaded template to a String, using context (if provided) to find the variables accessed during template rendering (e.g. strings to display).
Arguments: |
|
---|---|
Returns: | String |
The engine exposes an other method which may be useful in some cases (e.g. if you need a separate template namespace with, in OpenERP Web, Kanban views get their own QWeb2.Engine() instance so their templates don’t collide with more general “module” templates):
Loads a template file (a collection of templates) in the QWeb instance. The templates can be specified as:
A QWeb2.Engine() also exposes various attributes for behavior customization:
Prefix used to recognize directives during parsing. A string. By default, t.
Boolean flag putting the engine in “debug mode”. Normally, QWeb intercepts any error raised during template execution. In debug mode, it leaves all exceptions go through without intercepting them.
The jQuery instance used during template inheritance processing. Defaults to window.jQuery.
A Function. If present, called before compiling each DOM node to template code. In OpenERP Web, this is used to automatically translate text content and some attributes in templates. Defaults to null.
[1] | it is similar in that to Genshi, although it does not use (and has no support for) XML namespaces |
[2] | although it uses a few others, either for historical reasons or because they remain better fits for the use case. Odoo 8.0 still depends on Jinja and Mako. |
odoo docs theme based on Bootstrap 3.2 documentation adapted to Sphinx output with branding and color changes.