第二十五章 看板与高级视图

篇别:第三篇 数据库与数据管理

本章学习目标

  • 能配置 看板分组、排序、拖拽(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'})。分组字段多为 Many2oneSelection;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 看板按阶段分列与拖拽

25.1.4 本节练习

  1. 实操:图书看板按 category_id 分列,列内按 sequence 排序(列表与看板行为一致)。
  2. 简答last-write-wins 并发拖拽如何解决?(至少给出 乐观锁 / 消息子类型 / 串行化写 之一思路。)
  3. 判断:看板拖拽 从不 触发后端的 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 周视图拖拽改时间

25.2.4 本节练习

  1. 简答:无写权限用户拖拽 只读日历 会怎样?
  2. 实操:实现 跨午夜 的区间事件(区分 定时长事件all_day 字段用法)。
  3. 简答:日历与 时区(用户 vs UTC)至少列出 一条 测试清单项。

参考答案提示:1. 前端常禁止拖拽或保存时 AccessError。3. 例如:夏令时切换日附近创建事件,核对 UTC 存库与界面显示。


25.3 甘特图(Gantt)

25.3.1 知识要点

  • 依赖模块:标准 甘特 视图多出现在 项目、计划、工时 等企业应用或附加模块中;字段名常见 date_startdate_stopprogress依赖关系 可能为 M2M 或专用模型(以具体模块文档为准)。
  • 社区版:若无企业许可,可组合 日历 + 列表 + 数据透视 做简易排期;或采用 OCA 等第三方甘特模块(注意维护状态与版本兼容)。
  • 数据建模里程碑 常为 零时长或单日 记录;依赖箭头边表或 dependency 字段 表达,与简单区间事件不同。

25.3.2 案例

产品选型检查清单(简表)

问题 说明
许可 是否包含 Project / Planning 等企业应用
版本 视图 XML 标签与属性是否与 Odoo 19 一致
移动端 甘特是否在目标客户端可用

25.3.3 截图占位

图 25-3 甘特视图(企业/扩展模块)

25.3.4 本节练习

  1. 拓展:列出 两种 无甘特时的排期视图组合。
  2. 简答:甘特 依赖箭头里程碑 在数据建模上的差异?

参考答案提示: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 开发者工具 Performance 简测看板渲染

25.4.4 本节练习

  1. 实操:对 大量 卡片列表用浏览器 Performance 录一段,标出 最长任务 类型(布局、脚本、绘制)。
  2. 简答group_expand 展示全部空阶段,对 UX查询 各有什么影响?

参考答案提示:2. UX 上阶段一目了然;查询可能增加与空列相关的 read_group / search 成本。


本章综合练习

  1. 角色销售 更爱看板还是日历?调度员 呢?各写一句理由。
  2. 性能:看板 10 列 × 500 卡三条 优化思路(可含产品/架构取舍)。
  3. 综合借阅预约 同时需要 看板(状态)日历(到期),如何 共用模型字段 而不重复建模?
  4. 阅读:第八章 8.3 看板 与本章对照,用一句话说明 Odoo 19 card 迁移的注意点。
  5. 判断:日历视图中用户修改结束时间,一定触发 library.book@api.constrains。( )

参考答案提示:5. 对,只要最终 write 命中该模型且字段在约束中;若中间层 wizard 另说。


本章对应白皮书目录:第二十五章 看板与高级视图。