QWeb

QWeb - это основной генератор шаблонов, используемый Odoo[#othertemplates] _. Это генератор шаблонов XML[#genshif] _ и используется в основном для генерации HTML-фрагментов и страниц.

Директивы шаблона задаются в виде атрибутов XML с префиксом t-, например t-if для Условия, при котором элементы и другие атрибуты выводятся напрямую.

Чтобы избежать рендеринга элементов, также доступен элемент-заполнитель <t>, который выполняет свою директиву, но не генерирует никакого вывода сам по себе:

<t t-if="condition">
    <p>Test</p>
</t>

Создаст HTML следующего вида:

<p>Test</p>

Если condition являет true, но:

<div t-if="condition">
    <p>Test</p>
</div>

Создаст HTML следующего вида:

<div>
    <p>Test</p>
</div>

Вывод данных

QWeb имеет основную директиву вывода, которая автоматически выводит HTML-контент, ограничивающий риски XSS при отображении пользовательского контента: esc.

esc принимает выражение, оценивает его и печатает содержимое:

<p><t t-esc="value"/></p>

генерируется HTML со значением value, равным 42, и на выходе получится следующее:

<p>42</p>

Есть еще одна директива вывода raw, которая ведет себя так же, как и esc, но не очищает HTML-вывод. Может быть полезно отображать специально созданную разметку (например, из функций) или уже обработанную пользовательскую разметку.

Условия

У QWeb есть директива задания условия if, которая оценивает выражение, данное как значение атрибута:

<div>
    <t t-if="condition">
        <p>ok</p>
    </t>
</div>

Элемент рендерится, если условие true:

<div>
    <p>ok</p>
</div>

Но если условие false, оно удаляется из результата:

<div>
</div>

Условие применяется к тегу - носителю директивы, который не обязательно должна быть <t>:

<div>
    <p t-if="condition">ok</p>
</div>

даст те же результаты, что и в предыдущем примере.

Также доступны дополнительные условные директивы t-elif и t-else:

<div>
    <p t-if="user.birthday == today()">Happy birthday!</p>
    <p t-elif="user.login == 'root'">Welcome master!</p>
    <p t-else="">Welcome!</p>
</div>

Циклы

QWeb имеет директиву итерации foreach, которая принимает выражение, возвращающее коллекцию для итерации, и второй параметр t-as, предоставляющий имя, которое будет использоваться для «текущего элемента» итерации:

<t t-foreach="[1, 2, 3]" t-as="i">
    <p><t t-esc="i"/></p>
</t>

будет отрендерено как:

<p>1</p>
<p>2</p>
<p>3</p>

Подобно условиям, foreach применяется к элементу, несущему атрибут, и

<p t-foreach="[1, 2, 3]" t-as="i">
    <t t-esc="i"/>
</p>

вывод будет таким же как и в предыдущем примере.

foreach может выполнять перебор массива (текущим элементом будет текущее значение), сопоставление (текущий элемент будет текущим ключом) или целым числам (эквивалентно перебору массива между 0 включительно и предоставленным целым числом).

В дополнение к имени, переданному с помощью t-as, foreach предоставляет несколько других переменных для различных способов обработки данных:

$as_all (помечено как устаревшее)

объект перебора

$as_value
Текущее значение итерации, идентичное $as для списков и целых чисел, но для сопоставлений оно предоставляет значение (где $as предоставляет ключ)
$as_index
текущий индекс перебора (первый элемент перебора имеет индекс 0)
$as_size
размер коллекции, если она доступна
$as_first
является ли текущий элемент первым (эквивалентно $as_index == 0)
$as_last
является ли текущая позиция последней (эквивалентно $as_index + 1 == $as_size), требуется, чтобы размер коллекции был доступен
$as_parity (помечено как устаревшее)
либо "even" либо "odd", идет перебор либо четных либо нечетных элементов коллекции
$as_even (помечено как устаревшее)
флаг, указывающий на то, что текущий цикл итерации находится на четном индексе
$as_odd (помечено как устаревшее)
флаг, указывающий на то, что текущий цикл итерации находится на нечетном индексе

Эти дополнительные переменные, и все новые переменные, созданные в foreach, доступны только в области foreach. Если переменная существует вне контекста foreach, значение копируется в конце цикла foreach в глобальный контекст.

<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 может автоматически вычислять атрибуты и устанавливать результат вычисления на выходном узле. Это делается с помощью директивы t-att (attribute), которая существует в 3 разных формах:

t-att-$name

создается атрибут, соответствующий имени указанному в $name, значение атрибута вычисляется, результат устанавливается как значение созданного атрибута:

<div t-att-a="42"/>

будет отрендерено как:

<div a="42"></div>
t-attf-$name

представляет собой то же, что и предыдущий параметр, но является format string а не простым выражением, часто полезно для смешивания литералов и не литеральных строк (например, классов):

<t t-foreach="[1, 2, 3]" t-as="item">
    <li t-attf-class="row {{ (item_index % 2 === 0) ? 'even' : 'odd' }}">
        <t t-esc="item"/>
    </li>
</t>

будет отрендерено как:

<li class="row even">1</li>
<li class="row odd">2</li>
<li class="row even">3</li>
t-att=mapping

если параметр является сопоставлением, каждая пара (ключ, значение) создает новый атрибут и его значение:

<div t-att="{'a': 1, 'b': 2}"/>

будет отрендерено как:

<div a="1" b="2"></div>
t-att=pair

если параметр является парой (кортежем или массивом из 2 элементов), первым элементом пары является имя атрибута, а вторым его значение:

<div t-att="['a', 'b']"/>

будет отрендерено как:

<div a="b"></div>

Установка переменных

QWeb позволяет создавать переменные внутри шаблона, запоминать вычисления (использовать его несколько раз), назначать части данных более удобное имя, …

Это делается с помощью директивы set, которая принимает имя создаваемой переменной. Значение которой может быть представлено двумя способами:

  • с помощью атрибута t-value, содержащего выражение, и тогда результат его вычисления будет:

    <t t-set="foo" t-value="2 + 1"/>
    <t t-esc="foo"/>
    

    будет выведено 3

  • если атрибут t-value отсутствует, то будет отрендерено тело узла и установлено значение переменной:

    <t t-set="foo">
        <li>ok</li>
    </t>
    <t t-esc="foo"/>
    

    будет сгенерировано &lt;li&gt;ok&lt;/li&gt; (содержимое экранируется, так как мы использовали директиву esc)

Вызов суб-шаблонов

Шаблоны QWeb могут использоваться как для рендеринга верхнего уровня,так и для вызова из другого шаблона (чтобы избежать дублирования или для разбиения шаблона на составляющие) с помощью директивы t-call:

<t t-call="other-template"/>

Эта директива вызывает именованный шаблон с контекстом выполнения родителя, если other_template определен как:

<p><t t-value="var"/></p>

вызов выше будет рендериться как <p/> (без содержимого), а:

<t t-set="var" t-value="1"/>
<t t-call="other-template"/>

будет рендериться как <p>1</p>.

Однако это может привести к проблеме видимости за пределами t-call. В качестве альтернативы, контент, заданный в теле директивы call, будет выполнен до вызова суб-шаблона и может изменить локальный контекст

<t t-call="other-template">
    <t t-set="var" t-value="1"/>
</t>
<!-- "var" does not exist here -->

Тело директивы call может иметь произвольную сложность (а не только директивы set), и его отрендеренная форма будет доступна внутри вызываемого шаблона как магическая переменная 0:

<div>
    This template was called with content:
    <t t-raw="0"/>
</div>

будет вызван следующим образом:

<t t-call="other-template">
    <em>content</em>
</t>

Создаст HTML следующего вида:

<div>
    This template was called with content:
    <em>content</em>
</div>

Python

Эксклюзивные директивы

Бандлы ассетов

форматирование полей «смарт записей»

Директива t-field может быть использована только при выполнении доступа к полям (a.b) с помощью «смарт записи» (результат метода browse). Результат работы директивы автоматически форматируется в зависимости от типа поля и выводится в HTML в виде отформатированного текста.

t-options можно использовать для настройки полей, наиболее распространенной опцией является widget, другие опции зависят от поля или виджета.

Отладка

t-debug

Вызывает отладчик с помощью API-интерфейса PDB set_trace. Параметр должен быть именем модуля, в котором вызывается метод set_trace:

<t t-debug="pdb"/>

эквивалентно importlib.import_module("pdb").set_trace()

Помощники

Основанные на запросах

Большинство сценариев использования QWeb на стороне Python находятся в контроллерах (и во время выполнения HTTP-запросов). В этом случае шаблоны, хранящиеся в базе данных (как views), могут быть отрендерены вызовом метода odoo.http.HttpRequest.render():

response = http.request.render('my-template', {
    'context_value': 42
})

Это автоматически создает объект Response, который может быть возвращен с контроллера (или дополнительно настроен для соответствия).

Основанные на представлении

Более глубоки уровнем, по сравнению с предыдущим помощником, является метод render для ir.ui.view:

render(cr, uid, id[, values][, engine='ir.qweb][, context])

Создает представление/шаблон QWeb по id или external id. Шаблоны автоматически загружаются из записей модели ir.ui.view.

Устанавливает ряд значений по умолчанию в контексте рендеринга:

request
текущий объект WebRequest, если таковой имеется
debug
будет ли текущий запрос выполняться (если существует) в режиме debug
quote_plus
функция утилиты url-encoding
json
соответствующая стандартная библиотека
time
соответствующая стандартная библиотека
datetime
соответствующая стандартная библиотека
relativedelta
см. модуль
keep_query
вспомогательная функция keep_query
Параметры
  • values – значения контекста для передачи в QWeb для рендеринга
  • engine (str) – имя модели Odoo, используемой для рендеринга, можно использовать для расширения или настройки QWeb локально (создав «новый» qweb, основанный на «ir.qweb» с изменениями)

Javascript

Эксклюзивные директивы

Определение шаблонов

Директива t-name может быть размещена только на верхнем уровне файла шаблона (прямые наследники в корня документа)

<templates>
    <t t-name="template-name">
        <!-- template code -->
    </t>
</templates>

Она не принимает других параметров, но может использоваться с элементом <t> или любым другим. Если используется с элементом <t>, то должна иметь один дочерний элемент.

Имя шаблона является произвольной строкой, хотя, в случае использования нескольких связанных шаблонов (называются субшаблонами), принято использовать имена, разделенные точками, для указания иерархических отношений.

Наследование шаблонов

Наследование шаблона используется для изменения существующих шаблонов, например, для добавления информации в шаблоны, созданной другими модулями.

Наследование шаблонов выполняется с помощью директивы t-extend, которая принимает имя изменяемого шаблона в качестве параметра.

Когда t-extension объединяется с t-name, создается новый шаблон с заданным именем. В этом случае расширенный шаблон не изменяется, вместо этого директивы определяют, как создать новый шаблон.

Затем выполняется изменение с любым количеством суб-директив t-jquery:

<t t-extend="base.template">
    <t t-jquery="ul" t-operation="append">
        <li>new element</li>
    </t>
</t>

Директивы t-jquery принимают в качестве параметра селектор CSS. Этот селектор используется в расширенном шаблоне для выбора контекстных узлов, к которым применяется указанная t-operation:

append
тело элемента добавляется в конец контекстного элемента (после последнего дочернего элемента контекстного элемента)
prepend
тело элемента добавляется к элементу контекста (вставляется перед первым дочерним элементом контекстного элемента)
before
тело элемента вставляется непосредственно перед элементом контекста
after
тело элемента вставляется сразу после элемента контекста
inner
тело элемента замещает дочерние элементы контекстного элемента
replace
тело элемента используется для замены самого элемента контекста
attributes
Тело узлов должно состоять из любого количества элементов attribute, каждый из которых имеет атрибут name и текстовый контент; для именованного атрибута узла контекста будет установлено указанное значение (либо заменяется, если оно уже существовало или добавлено если нет)
No operation

если не указано `` t-operation``, тело шаблона интерпретируется как javascript-код и выполняется с контекстным элементом как this

Отладка

В реализации QWeb для javascript есть несколько отладочных инструментов:

t-log

принимает в качестве параметра выражение , вычисляет значение выражения во время рендеринга и записывает его результат с помощью console.log:

<t t-set="foo" t-value="42"/>
<t t-log="foo"/>

выведет 42 в консоль

t-debug

запускает точку останова отладчика во время рендеринга шаблона:

<t t-if="a_test">
    <t t-debug="">
</t>

становит выполнение, если активен режим отладки (точное состояние зависит от браузера и его инструментов разработки)

t-js

тело элемента - код javascript, выполняемый во время рендеринга шаблона. Принимает параметр context, который является именем, под которым контекст рандеринга будет доступен в теле t-js:

<t t-set="foo" t-value="42"/>
<t t-js="ctx">
    console.log("Foo is", ctx.foo);
</t>

Помощники

core.qweb

(сore это модуль web.core). Экземпляр QWeb2.Engine() со всеми загруженными файлами шаблонов определенных в модулях и ссылки на стандартные вспомогательные объекты _ (подчеркивание), _t (функция перевода) и JSON.

с помощью функции core.qweb.render можно легко отрендерить базовые шаблоны модулей

API

class QWeb2.Engine()

QWeb «renderer», обрабатывает большую часть логики QWeb (загрузка, парсинг, компиляция и рендеринг шаблонов).

OpenERP Web создает экземпляр для пользователя и устанавливает его в core.qweb. Он также загружает все файлы шаблонов различных модулей в этот экземпляр QWeb.

Класс QWeb2.Engine() также служит в качестве пространства имен для шаблонов.

QWeb2.Engine.QWeb2.Engine.render(template[, context])

Отправляет ранее загруженный шаблон в String, используя context (если имеется), чтобы найти переменные, к которым обращался при рендеринге данного шаблона (например, строки для отображения).

Аргументы
  • template (String) – имя шаблона для рендеринга
  • context (Object) – основное пространство имен, используемое для рендеринга шаблона
Результат
Строка

Генератор шаблонов предоставляет другой метод, который может быть полезен в некоторых случаях (например, если вам нужно отдельное пространство имен для ваших шаблонов, в OpenERP Web, представление Kanban получит свой собственный класс QWeb2.Engine(), чтобы их шаблоны не пересекались с основными шаблонами «модулей»):

QWeb2.Engine.QWeb2.Engine.add_template(templates)

Загружает файл шаблона (коллекцию шаблонов) в экземпляр QWeb. Шаблоны могут быть указаны как:

Строка XML
QWeb попытается произвести его парсинг в документ XML, а затем загрузить его.
URL-адрес
QWeb попытается загрузить содержимое URL, а затем загрузить полученную XML-строку.
Document или Node
QWeb будет преодолевать первый уровень документа (дочерние элементы предоставленного корня) и загружать любой именованный шаблон или переопределение шаблона.

Класс QWeb2.Engine() также предоставляет различные атрибуты для настройки поведения:

QWeb2.Engine.QWeb2.Engine.prefix

Префикс, используемый для распознавания директив при парсинге. Строка. По умолчанию, t.

QWeb2.Engine.QWeb2.Engine.debug

Флаг, переводящий генератор шаблонов в режим отладки. Обычно QWeb перехватывает любую ошибку, возникающую при выполнении шаблона. В режиме отладки все исключения проходят без перехвата.

QWeb2.Engine.QWeb2.Engine.jQuery

Экземпляр jQuery, используемый при обработке наследования шаблонов. По умолчанию используется window.jQuery.

QWeb2.Engine.QWeb2.Engine.preprocess_node

Function. Если присутствует, вызывается перед компиляцией каждого узла DOM в код шаблона. В Odoo Web это используется для автоматического перевода текстового содержимого и некоторых атрибутов в шаблонах. По умолчанию используется null.

[1] похож на Genshi, хотя он не использует (и не имеет поддержки) пространства имён XML
[2] хотя он использует несколько других, либо по историческим причинам, либо потому, что они лучше подходят для текущего момента. Odoo 9.0 по-прежнему зависит от Jinja и Mako.