第八章 视图(Views)
篇别:第一篇 基础知识
本章学习目标
- 能独立编写 form / list / search 及常用分析视图,并理解
arch与模型字段 的对应关系。 - 掌握 看板 card 化、pivot/graph、视图继承与 priority 等升级敏感点。
- 能说明 grid、settings、activity 等视图的 依赖前提(mixin、模块、字段)。
- 具备 xpath 调试 与「升级后视图报错」的基本排查能力。
导读:视图是什么
视图是存储在 ir.ui.view 中的 XML 架构(arch),描述 字段布局、按钮、子视图、装饰与属性。同一模型可有 多套视图(不同 xmlid/类型/priority),由 ir.actions.act_window 的 view_mode 与 view_id 等决定默认打开哪一套。
8.1 表单视图(Form)
8.1.1 知识要点
- 根节点
<form>;常用子结构:<header>(按钮+状态)、<sheet>(主内容)、<chatter>(若模型继承 mail)。 <group>:两列表格布局;<notebook>/<page>分页签。- 字段属性:
readonly、required、invisible、placeholder、options、widget(见第五章)。 invisible表达式:可用attrs旧式或 Odoo 17+ 域表达式风格(以项目版本为准)。
8.1.2 案例
<form string="图书">
<header>
<button name="action_confirm" type="object" string="确认" class="btn-primary"
invisible="state != 'draft'"/>
<field name="state" widget="statusbar" statusbar_visible="draft,published,archived"/>
</header>
<sheet>
<div class="oe_title">
<label for="name"/>
<h1><field name="name" placeholder="书名"/></h1>
</div>
<group>
<group string="基本信息">
<field name="isbn"/>
<field name="category_id"/>
</group>
<group string="馆藏">
<field name="copy_count"/>
</group>
</group>
<notebook>
<page string="借阅记录" name="loans">
<field name="loan_line_ids">
<list>
<field name="partner_id"/>
<field name="loan_date"/>
</list>
</field>
</page>
</notebook>
</sheet>
</form>
8.1.3 截图占位

8.1.4 本节练习
- 实操:为图书表单增加第二个 page「备注」,放
note文本字段。 - 简答:
button的invisible与states属性(若仍可用)相比,推荐哪种维护方式? - 判断:表单里没有写的字段,用户一定不能改。( )(提示:其他视图或 RPC)
参考答案提示:3. 错。
8.2 列表视图(List/Tree)
8.2.1 知识要点
- 根节点
<list>(历史名tree仍可能被旧代码使用)。 editable="top|bottom":行内编辑;注意 并发写 与 onchange 性能。default_order、limit、create、delete、multi_edit。decoration-*:基于行数据 CSS 类;与 colors(旧)区分。
8.2.2 案例
<list string="图书" editable="bottom" default_order="sequence,name"
decoration-muted="state=='archived'"
decoration-danger="not active">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="state"/>
<field name="copy_count" sum="合计册数"/>
</list>
8.2.3 截图占位

8.2.4 本节练习
- 简答:
editable打开时,对 记录规则 与 批量导入 各有什么影响? - 实操:开启
multi_edit后多选两行批量改某一字段,观察一次write还是多次。
8.3 看板视图(Kanban)(补充)
8.3.1 知识要点
Odoo 19 推荐 <kanban> 内使用 <card> 等结构化标签,替代旧 <kanban-box>;具体标签名以官方模块 crm、project 为准。
看板常配合 default_group_by(在 action context)按 Many2one / Selection 分列。
8.3.2 案例(结构示意)
<kanban default_group_by="category_id" class="o_kanban_mobile">
<field name="id"/>
<field name="name"/>
<field name="state"/>
<templates>
<t t-name="card">
<div class="oe_kanban_card oe_kanban_global_click">
<field name="name"/>
<field name="state" widget="badge"/>
</div>
</t>
</templates>
</kanban>
实际 t-name 与 card 写法请复制当前版本 project 模块模板。
8.3.3 截图占位

8.3.4 本节练习
- 实操:将旧模块
kanban-box按官方示例改为新结构并-u。 - 简答:看板
quick_create依赖什么?
8.4 搜索视图(Search)
8.4.1 知识要点
<field name="..."/>:可搜索列;可设filter_domain。<filter>:预置 domain;name供context中search_default_xxx使用。<group>+context="{'group_by': ...}":分组。<searchpanel>:侧栏筛选(依赖字段与模块)。
8.4.2 案例
<search>
<field name="name" string="书名/ISBN" filter_domain="['|',('name','ilike',self),('isbn','ilike',self)]"/>
<separator/>
<filter name="draft" string="草稿" domain="[('state','=','draft')]"/>
<filter name="published" string="已出版" domain="[('state','=','published')]"/>
<group expand="0" string="分组">
<filter name="grp_category" string="分类" context="{'group_by':'category_id'}"/>
<filter name="grp_state" string="状态" context="{'group_by':'state'}"/>
</group>
</search>
动作中默认勾选草稿
<field name="context">{'search_default_draft': 1}</field>
8.4.3 截图占位

8.4.4 本节练习
- 实操:增加
search_default_published与searchpanel(若字段适合)。 - 简答:
filter_domain中的self指什么?
参考答案提示:2. 当前搜索框输入值。
8.5 日历视图(Calendar)
8.5.1 知识要点
date_start 必填;date_stop、date_delay、all_day、color、mode(month/week/day)。拖拽改时间会 write 对应字段。
8.5.2 案例
<calendar string="借阅日历" date_start="start_date" date_stop="end_date"
color="partner_id" mode="month" quick_create="0">
<field name="name"/>
<field name="partner_id"/>
</calendar>
8.5.3 截图占位

8.5.4 本节练习
- 实操:定义冲突检测:
end_date < start_date用@api.constrains拦截。 - 简答:无
write权限的用户拖拽事件会怎样?
8.6 数据透视表(Pivot)(补充)
8.6.1 知识要点
行/列维度 + 度量;底层与 read_group 同源思路;Odoo 19 可能对 GROUPING SETS 等有增强,以文档为准。
适合 财务、销售分析;大数据量注意 超时与预聚合。
8.6.2 案例
在带 sale.report 类模型的模块中打开 pivot,切换度量与「翻转行/列」。
8.6.3 截图占位

8.6.4 本节练习
- 简答:pivot 与 graph 的选型差异?
- 拓展:用
read_group在 Python 中复现 pivot 某一切片。
8.7 图表视图(Graph)
8.7.1 知识要点
type="bar|line|pie",stacked,order,measure 字段类型需可聚合。
8.7.2 案例
<graph string="借阅统计" type="bar" stacked="True">
<field name="category_id" type="row"/>
<field name="loan_count" type="measure"/>
</graph>
8.7.3 截图占位

8.7.4 本节练习
- 实操:将
type改为pie观察度量限制。 - 判断:graph 可直接编辑数据。( )
8.8 活动视图(Activity)
8.8.1 知识要点
模型需 _inherit mail.activity.mixin;视图中展示待办时间线。常与 calendar 联用。
8.8.2 案例
参考 sale.order:activity_ids 与 activity_exception_decoration 等字段(以版本为准)。
8.8.3 截图占位

8.8.4 本节练习
- 实操:在图书上
activity_schedule一条下周待办并在活动视图查看。 - 简答:无 mixin 能否硬写
activity视图?
参考答案提示:2. 不应;缺模型能力与字段。
8.9 队列分析(Cohort)
8.9.1 知识要点
按 首次事件日期 分桶,观察后续周期留存;需清晰定义 cohort 日期字段 与 留存度量。
8.9.2 案例
在分析应用中切换 周/月 粒度,与 pivot 同一指标对比阅读体验。
8.9.3 截图占位

8.9.4 本节练习
- 简答:Cohort 与 pivot 在「时间维度」上的根本不同?
- 情景:B2B 长周期成交,cohort 分桶过细有何问题?
8.10 甘特图(Gantt)
8.10.1 知识要点
依赖 项目/计划 类模块与企业版或特定扩展;字段:date_start、date_stop、progress、depends(视模块)。
8.10.2 案例
创建两条依赖任务,观察连线(环境支持时)。
8.10.3 截图占位

8.10.4 本节练习
- 拓展:社区版无甘特时,用 calendar + list + graph 如何凑合项目管理?
8.11 地图视图(Map)
8.11.1 知识要点
需 地址或经纬度 字段、地理编码服务、对应 地图模块 与合法 API 密钥(合规存储在系统参数)。
8.11.2 案例
为伙伴维护完整地址,地图聚类显示。
8.11.3 截图占位

8.11.4 本节练习
- 实操:列出启用地图的 配置清单(参数名可空,占位即可)。
- 简答:地图视图的隐私与 GDPR 注意点?
8.12 网格视图(Grid)(新增)
8.12.1 知识要点
二维矩阵(常见:人 × 日)批量录入;依赖 timesheet/grid 类模块与约定字段。
8.12.2 案例
对比 grid 单元格 与 list editable 在录入 20×7 格数据时的效率。
8.12.3 截图占位

8.12.4 本节练习
- 简答:Grid 与 pivot 的「只读分析」有何不同?
8.13 设置视图(Settings)(新增)
8.13.1 知识要点
res.config.settings TransientModel;字段 config_parameter 持久化到 ir.config_parameter;布尔常用 implied_group 启模块。
8.13.2 案例
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
library_max_loan_days = fields.Integer(
string='最大借阅天数',
config_parameter='library.max_loan_days',
default=30,
)
8.13.3 截图占位

8.13.4 本节练习
- 实操:借阅确认时读取
ir.config_parameter校验天数。 - 简答:为何设置类字段多放在 TransientModel?
8.14 视图继承(新增)
8.14.1 知识要点
inherit_id指向父视图;arch内用 xpath 定位节点。position:inside、after、before、replace、attributes。priority:数值越小越先应用(以官方说明为准);多模块改同一视图时 协调 priority 避免互相覆盖。- 稳定锚点:优先
name属性、data-key(若有),避免脆弱路径。
8.14.2 案例
<record id="view_partner_form_inherit_library" model="ir.ui.view">
<field name="name">res.partner.form.inherit.library</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="priority">20</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='function']" position="after">
<field name="is_library_member"/>
</xpath>
<xpath expr="//sheet" position="inside">
<div class="alert alert-info" invisible="not is_library_member">
该联系人为图书馆注册读者。
</div>
</xpath>
</field>
</record>
8.14.3 截图占位

8.14.4 本节练习
- 实操:用
position="attributes"给某字段加readonly。 - 简答:大段
replace的风险? - 故障:升级报 xpath 找不到节点——列出 5 步 排查。
参考答案提示:2. 上游结构变更即碎。3. 激活开发者模式看合并 arch、查父视图是否变、priority、模块是否加载、XML id 是否正确。
本章综合练习
- 为
library.book配置 list + form + search + kanban 四套视图,并写入 一个act_window。 - 继承:在 两个 独立模块中继承同一表单,通过 priority 控制字段顺序并截图合并结果。
- 性能:列表展示 10 万 条时,仅靠视图层能做什么?ORM/DB 层需做什么?
- 文档化:为你团队写「视图评审 checklist」5 条(字段权限、xpath、移动端、翻译、无障碍任选角度)。
本章对应白皮书目录:第八章 视图。