第二十五章 看板与高级视图
篇别:第三篇 数据库与数据管理
本章学习目标
- 能配置 看板分组、排序、拖拽(sequence / handle) 及与 表单、列表 的联动。
- 理解 日历视图 的 拖拽写库 与 权限、约束、时区 的关系。
- 了解 甘特图 的 模块前提 与社区版 替代方案。
- 能分析 看板列内大量卡片 时的 性能 与 limit、domain、read_group 策略。
- 能将本章与 第八章 视图 对照,说出 Odoo 19 看板
<card>等与旧写法的差异。
导读:看板与日历是「调度型」视图
看板(Kanban) 强调 阶段泳道 与 卡片拖拽,适合流程可视化(销售阶段、工单状态、借阅处理等)。日历(Calendar) 强调 时间轴与预约,适合资源预订、到期提醒。二者都常在用户操作后触发 write,需要与 状态机、mail.activity、资源冲突检测 协同设计;忽略并发与权限时,容易出现「界面拖得动、保存报错」或 last-write-wins 覆盖他人修改等问题。
本章按 看板 → 日历 → 甘特 → 性能 顺序组织;甘特在企业版或扩展模块中更常见,社区项目需提前评估 许可与依赖。
25.1 看板拖拽排序
25.1.1 知识要点
- 分组列:列来自
default_group_by(或在 窗口动作的context里指定,如{'group_by': 'stage_id'})。分组字段多为 Many2one 或 Selection;Many2one 空值通常进入 「未设置」 列。 group_expand(可选):在分组字段所在模型上定义方法,可 强制列出所有阶段(含暂无记录的列),改善 UX,但可能增加查询;需权衡 空列数量 与 SQL 次数。- 排序:模型上常用
sequence(Integer);列表视图配合widget="handle"可拖拽行序。看板内拖拽常更新sequence或 阶段字段stage_id,具体取决于业务(跨列拖 = 改阶段,列内拖 = 改序号)。 - 颜色与标签:
color_field指向 整型颜色索引(若当前版本视图支持);标签可用 many2many_tags 或看板模板内自定义展示。 - 快速创建:
quick_create="1"(或关闭)控制列内快速建单;复杂业务可关 quick create,改为 「新建」打开完整表单。 - Odoo 19 结构提示:看板 XML 正逐步统一为
<card>等结构(详见第八章 8.3);升级旧模块时注意对照官方 View 文档 与现有web模块中的标准视图。
25.1.2 案例
动作 context 中默认按阶段分组(示意):
<record id="action_library_book_kanban" model="ir.actions.act_window">
<field name="name">图书看板</field>
<field name="res_model">library.book</field>
<field name="view_mode">kanban,list,form</field>
<field name="context">{'group_by': 'stage_id'}</field>
</record>
看板根节点与模型字段(片段):
<kanban default_group_by="stage_id" class="o_kanban_small_column" quick_create="0">
<field name="id"/>
<field name="name"/>
<field name="stage_id"/>
<field name="sequence"/>
<templates>
<t t-name="card">
<div class="oe_kanban_card oe_kanban_global_click">
<strong><field name="name"/></strong>
</div>
</t>
</templates>
</kanban>
sequence = fields.Integer(default=10)
stage_id = fields.Many2one('library.book.stage')
搜索视图 中可提供 默认分组 的 filter(与 search_default_* 配合),便于用户一键恢复「按阶段」视图。
25.1.3 截图占位

25.1.4 本节练习
- 实操:图书看板按
category_id分列,列内按sequence排序(列表与看板行为一致)。 - 简答:last-write-wins 并发拖拽如何解决?(至少给出 乐观锁 / 消息子类型 / 串行化写 之一思路。)
- 判断:看板拖拽 从不 触发后端的
write。( )
参考答案提示:3. 错;改阶段或顺序通常会 write。
25.2 日历视图
25.2.1 知识要点
date_start必填;date_stop可选(单日事件可省略);all_day与 全天事件 相关;mode控制默认 月/周/日 等(以版本为准)。- 颜色字段:
color可映射 partner 或自定义整型,用于区分资源。 - 拖拽与缩放:用户改变起止时间 → 前端发起
write;需perm_write且 记录规则 允许,否则会报错或无反馈。 - 冲突检测:会议室、设备借用等场景,用
@api.constrains在 Python 层检测重叠区间;高并发可结合 数据库排他约束(需自行评估 PostgreSQL EXCLUDE 等方案)。 - 时区:数据库存 UTC;界面按 用户时区 展示。跨日「全天」事件在边界时区容易 偏移一天,测试时务必用 多时区用户 各点一次。
25.2.2 案例
会议室预约冲突约束(与上一版一致,注意运算符在 Python 源码中直接使用 < >):
@api.constrains('start_datetime', 'end_datetime', 'room_id')
def _check_room_overlap(self):
for rec in self:
if not rec.room_id or not rec.start_datetime or not rec.end_datetime:
continue
domain = [
('room_id', '=', rec.room_id.id),
('id', '!=', rec.id),
('start_datetime', '<', rec.end_datetime),
('end_datetime', '>', rec.start_datetime),
]
if self.search_count(domain):
raise ValidationError(_('该会议室时段已被占用'))
日历视图最小 XML(示意):
<calendar string="预约" date_start="start_datetime" date_stop="end_datetime" mode="week" color="room_id">
<field name="name"/>
<field name="room_id"/>
</calendar>
25.2.3 截图占位

25.2.4 本节练习
- 简答:无写权限用户拖拽 只读日历 会怎样?
- 实操:实现 跨午夜 的区间事件(区分 定时长事件 与 all_day 字段用法)。
- 简答:日历与 时区(用户 vs UTC)至少列出 一条 测试清单项。
参考答案提示:1. 前端常禁止拖拽或保存时 AccessError。3. 例如:夏令时切换日附近创建事件,核对 UTC 存库与界面显示。
25.3 甘特图(Gantt)
25.3.1 知识要点
- 依赖模块:标准 甘特 视图多出现在 项目、计划、工时 等企业应用或附加模块中;字段名常见
date_start、date_stop、progress,依赖关系 可能为 M2M 或专用模型(以具体模块文档为准)。 - 社区版:若无企业许可,可组合 日历 + 列表 + 数据透视 做简易排期;或采用 OCA 等第三方甘特模块(注意维护状态与版本兼容)。
- 数据建模:里程碑 常为 零时长或单日 记录;依赖箭头 需 边表或 dependency 字段 表达,与简单区间事件不同。
25.3.2 案例
产品选型检查清单(简表):
| 问题 | 说明 |
|---|---|
| 许可 | 是否包含 Project / Planning 等企业应用 |
| 版本 | 视图 XML 标签与属性是否与 Odoo 19 一致 |
| 移动端 | 甘特是否在目标客户端可用 |
25.3.3 截图占位

25.3.4 本节练习
- 拓展:列出 两种 无甘特时的排期视图组合。
- 简答:甘特 依赖箭头 与 里程碑 在数据建模上的差异?
参考答案提示:1. 例如:日历 + 列表;看板阶段 + 截止日期字段排序。
25.4 性能与大数据
25.4.1 知识要点
- 看板每列卡片过多:DOM 与图片/头像会导致 卡顿;可 缩小默认 domain、分页/limit(视实现)、禁用不必要的 avatar、统计小部件。
- 日历月视图:宜 按可见范围拉取 事件,避免一次加载全年。
- 后端:
read_group可做 列头计数、汇总,减少前端逐卡read;注意 权限 与 规则 对聚合结果的影响。 - 索引:常作为 分组、排序、日历 range 查询 的字段应加
index=True或复合索引(迁移脚本)。
25.4.2 案例
列头统计思路(伪代码):打开看板前由客户端动作或 ir.actions.act_window 的 近端逻辑 调用 read_group,将结果注入 context 或 专用 service(进阶);简单场景可用 搜索面板 或 单独 KPI 区域。
25.4.3 截图占位

25.4.4 本节练习
- 实操:对 大量 卡片列表用浏览器 Performance 录一段,标出 最长任务 类型(布局、脚本、绘制)。
- 简答:
group_expand展示全部空阶段,对 UX 与 查询 各有什么影响?
参考答案提示:2. UX 上阶段一目了然;查询可能增加与空列相关的 read_group / search 成本。
本章综合练习
- 角色:销售 更爱看板还是日历?调度员 呢?各写一句理由。
- 性能:看板 10 列 × 500 卡 的 三条 优化思路(可含产品/架构取舍)。
- 综合:借阅预约 同时需要 看板(状态) 与 日历(到期),如何 共用模型字段 而不重复建模?
- 阅读:第八章 8.3 看板 与本章对照,用一句话说明 Odoo 19 card 迁移的注意点。
- 判断:日历视图中用户修改结束时间,一定触发
library.book的@api.constrains。( )
参考答案提示:5. 对,只要最终 write 命中该模型且字段在约束中;若中间层 wizard 另说。
本章对应白皮书目录:第二十五章 看板与高级视图。