Odoo QWeb 模板引擎详解与实战
Odoo 的 QWeb 是其自研的模板引擎,广泛应用于 HTML、XML、PDF 等内容的生成,支撑了前端页面渲染、报表输出、门户页面、邮件模板等多种场景。本文将系统介绍 QWeb 的核心用法、工作原理,并通过实战案例演示如何在门户页面实现表单提交与数据存储。
一、QWeb 简介
QWeb 是 Odoo 原生的 XML/HTML 模板引擎,采用 XML 语法,通过丰富的 t- 前缀指令(如 t-foreach
、t-if
、t-call
等)实现模板逻辑控制。与 Jinja2 相比,QWeb 更加结构化、安全,适合可视化编辑器和网页构建器场景。
QWeb 支持两大类渲染方式:
- 前端渲染:用于 Web 页面(如门户、报表、邮件预览等)
- 后端渲染:用于生成 PDF 报表、邮件内容等(通过 Python 代码调用)
二、QWeb 核心指令(t-指令)
指令 | 用法 | 示例 |
---|---|---|
t-foreach | 遍历 | <t t-foreach="docs" t-as="doc">...</t> |
t-if / t-elif / t-else | 条件判断 | <t t-if="doc.state == 'done'">完成</t> |
t-set | 设置变量 | <t t-set="total" t-value="sum(docs.mapped('amount'))"/> |
t-call | 引用模板 | <t t-call="module_name.template_name"/> |
t-field | 渲染字段 | <span t-field="doc.partner_id"/> |
t-esc | 转义输出 | <span t-esc="doc.name"/> |
t-raw | 原样输出(不转义) | <div t-raw="doc.html_description"/> |
三、QWeb 应用场景
1. Web 前端视图(门户、自定义页面等)
QWeb 模板常用于 ir.ui.view
(type=‘qweb’)视图,广泛应用于门户表单、公共页面等。例如:
<template id="my_portal_form" name="My Portal Form"><form action="/my/form/submit" method="post"><input type="text" name="name"/><t t-if="user_id"><input type="hidden" name="user_id" t-att-value="user_id"/></t></form>
</template>
通过控制器渲染模板:
return request.render('my_module.my_portal_form', {'user_id': user.id})
2. 报表生成(PDF 报表)
Odoo 利用 QWeb 生成 PDF 报表(如发票、订单等),通过 report_qweb
模块实现。
模板定义:
<template id="report_order_document"><t t-call="web.external_layout"><t t-set="doc" t-value="docs[0]"/><div class="page"><h2>订单号:<span t-field="doc.name"/></h2></div></t>
</template>
渲染调用:
return self.env.ref('sale.report_saleorder').report_action(self)
3. 控制器网页(如公开网站或门户)
结合 Web Controller 使用:
@http.route('/public/page', type='http', auth='public', website=True)
def public_page(self, **kwargs):return request.render('my_module.public_template', {'data': some_data})
四、QWeb 工作原理
1. 模板加载
QWeb 模板存储于 ir.ui.view
表(type=‘qweb’),Odoo 启动时会将其缓存至内存,供后续渲染调用。
2. 渲染引擎
QWeb 的核心为 QWebEngine
(Python 实现):
- 解析模板为 DOM 树结构
- 根据上下文渲染 t-指令
- 输出最终 HTML 字符串
常见调用方式:
- Web 控制器:
request.render(template_name, context)
- 后端报表:
self.env.ref('module.report_template_id').render(context)
3. 继承机制(t-inherit)
QWeb 支持模板继承,类似 XML 视图继承:
<template id="my_template_inherit" inherit_id="base.template_to_inherit"><xpath expr="//div[@id='target']" position="replace"><div>新的内容</div></xpath>
</template>
五、开发与调试建议
- 开启开发者模式,便于查看 QWeb 模板结构
- 使用
<t t-debug="1">
触发前端调试工具 - 报表调试可用
--log-level=debug
输出渲染上下文 t-field
可自动格式化输出复杂字段(如货币、日期)
六、实战案例:门户表单 + 控制器
1. 业务场景
实现门户页面 /project/apply
,展示项目申请表,用户提交后由控制器处理并保存至数据库。
2. 数据模型
# models/project_application.py
from odoo import models, fieldsclass ProjectApplication(models.Model):_name = 'project.application'_description = 'Project Application'name = fields.Char(string='Applicant Name', required=True)email = fields.Char(string='Email')project_name = fields.Char(string='Project Name')description = fields.Text(string='Project Description')
3. QWeb 表单模板
<!-- views/project_application_templates.xml -->
<odoo><template id="project_application_form" name="Project Application Form"><t t-call="website.layout"><div class="container mt-5"><h2>项目申请表</h2><form action="/project/apply/submit" method="post" class="form-group"><input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/><div class="form-group"><label>姓名</label><input type="text" name="name" class="form-control" required/></div><div class="form-group"><label>Email</label><input type="email" name="email" class="form-control"/></div><div class="form-group"><label>项目名称</label><input type="text" name="project_name" class="form-control"/></div><div class="form-group"><label>项目描述</label><textarea name="description" class="form-control"></textarea></div><button type="submit" class="btn btn-primary mt-2">提交申请</button></form></div></t></template><template id="project_application_success" name="Application Success"><t t-call="website.layout"><div class="container mt-5"><h2>提交成功!</h2><p>感谢您的申请,我们会尽快处理。</p></div></t></template>
</odoo>
4. 控制器逻辑
# controllers/main.py
from odoo import http
from odoo.http import requestclass ProjectApplicationController(http.Controller):@http.route('/project/apply', type='http', auth='public', website=True)def apply_form(self, **kwargs):return request.render('your_module_name.project_application_form')@http.route('/project/apply/submit', type='http', auth='public', website=True, csrf=True)def apply_form_submit(self, **post):request.env['project.application'].sudo().create({'name': post.get('name'),'email': post.get('email'),'project_name': post.get('project_name'),'description': post.get('description'),})return request.render('your_module_name.project_application_success')
5. Manifest 配置
# __manifest__.py
'data': ['views/project_application_templates.xml',
],
6. 访问测试
启动 Odoo 后,访问 http://localhost:8069/project/apply
,填写并提交表单,系统将保存数据并显示成功页面。
七、进阶建议
功能 | 实现方式 |
---|---|
表单校验 | HTML5 校验 + 控制器后端校验 |
自动填充用户信息 | 控制器中通过 request.env.user 获取当前用户信息 |
文件上传 | 表单添加 enctype="multipart/form-data" ,后端用 request.httprequest.files 处理 |
提交后跳转页面 | 使用 return request.redirect('/thank-you') |
八、总结
项目 | 描述 |
---|---|
渲染引擎 | QWebEngine(Python 层) |
应用场景 | Web 页面、报表模板、控制器页面、门户表单 |
优点 | 安全、结构化、可嵌套、支持继承 |
与 Jinja2 比较 | 更偏向 XML 结构控制,适合 B2B 页面、门户、报表;Jinja 更灵活 |