先说结论
很多系统做“待办”功能时,第一反应都是新建一个模型:
- 任务是一个模型
- 私人待办再单独一个模型
但 Odoo 官方没有这么走。
从 addons/project_todo/models/project_task.py 和 res_users.py 看,To-Do 依然建立在 project.task 之上,只是通过:
- 特定视图
- systray 活动分组
- 名称生成逻辑
- onboarding 自动任务
把“私人待办”包装成一种不同体验。
一句话说:
Odoo To-Do 不是新数据模型,而是
project.task的一层产品壳。
这个思路对协同办公产品非常有启发。
为什么官方没有再造一个 todo.task
从建模角度看,To-Do 和 Task 当然可以拆。
但 Odoo 选择复用 project.task,本质上是在保留几件非常值钱的能力:
- 现成的活动、消息、负责人、截止日期能力
- 和项目任务之间的自然转换关系
- 不必复制一套权限、搜索、看板、日历、活动逻辑
也就是说,官方判断是:
- 待办和任务在行为上高度相近
- 真正不同的,主要是产品入口和使用场景
所以更划算的方式不是“再造一套对象”,而是“同一个对象,多一种壳层”。
create() 为什么会自动生成待办标题
project_todo/models/project_task.py 里最先值得看的就是 create()。
它的逻辑很朴素,但非常有产品感:
- 如果没有
name - 又没有
project_id - 也没有
parent_id - 那它倾向把这条记录视为更像 To-Do
这时:
- 若有
description,就从描述首行抽标题 - 若连描述都没有,就给一个
Untitled to-do
这说明官方对 To-Do 的用户预期非常清楚:
待办往往比正式项目任务更轻、更随手、更接近“先记下来”。
所以标题可以从正文里懒生成,而不强制用户一开始就严肃命名。
这是典型的协同办公产品手感设计。
为什么官方专门做了一组 To-Do 视图
同一个模型,不代表必须同一种界面。
get_todo_views_id() 返回了一整组 To-Do 专用视图:
- kanban
- list
- form
- calendar
- activity
这说明 Odoo 的思路是:
- 数据层复用
project.task - 体验层单独给 To-Do 一套主视图编排
这很重要。
因为 To-Do 的典型用户心智和项目任务不完全一样:
To-Do 更强调
- 快速捕捉
- 今天要做什么
- 个人节奏
- 轻量整理
Project Task 更强调
- 项目上下文
- 多人协作
- 阶段流转
- 依赖与交付
所以 Odoo 并没有把它们数据上拆开,而是把体验上拆开。
Systray 为什么要把 Task 和 To-Do 分开统计
project_todo/models/res_users.py 里 _get_activity_groups() 很值得看。
它做了一件很有代表性的事:
- 先把原来统一的
project.task活动组拿掉 - 再按
BOOL(t.project_id) AS is_task重新拆成两类 - 有项目的叫 Task
- 没项目的叫 To-Do
这说明在官方产品心智里:
To-Do 和 Task 可以共用一张表,但在提醒入口上必须被视为两种不同工作负载。
这点特别真实。
因为对一个用户来说:
- 今天我要回复的私人待办
- 和项目里需要协同推进的任务
虽然都叫 task,但心理负担完全不同。
如果全部混在同一个提醒桶里,反而不好用。
为什么 project_id 成了两种语义的分水岭
从这套实现可以看出,Odoo 实际上把:
- 有项目上下文 的
project.task看作正式任务 - 无项目上下文 的
project.task看作更偏个人待办
这不是绝对真理,但它是一个非常聪明的产品切线。
因为它避免了:
- 额外建模
- 额外迁移
- 额外同步
又能让产品自然形成两种心智空间。
对很多企业内部协同系统来说,这是很值得借鉴的:
先问“是不是只差一个上下文”,再决定要不要新建模型。
为什么 To-Do 能一键转成正式 Task
action_convert_to_task() 做的事情看似简单:
- 把记录打回
project.task表单打开 - 同时同步公司上下文
但这件事的产品意义很大。
因为它说明 Odoo 不是把 To-Do 和 Task 切成两座孤岛,而是明确承认:
- 很多正式任务,最初就是一条私人待办
- 很多“先记一下”的事,后来会升级成项目事项
所以最好的模型不是两套对象来回复制,而是:
- 让轻任务自然长成重任务
这正是协同办公里非常常见的工作演化路径。
为什么新用户入职时会自动生成 To-Do
_generate_onboarding_todo() 更能体现官方对 To-Do 的定位。
它会在用户 onboarding 时:
- 渲染一段欢迎模板
- 生成标题
Welcome %s! - 直接创建一条
project.task - 且用
mail_auto_subscribe_no_notify降低干扰
这说明官方把 To-Do 看成:
- 最适合承载“个人启动清单”的容器
- 比正式项目任务更适合做新手引导
换句话说,To-Do 在 Odoo 里不仅是“轻任务”,还是个人工作入口。
这套设计对协同产品有什么启发
1. 不要看到新场景就先建新模型
如果核心行为高度相似,先考虑“壳层分化”而不是“数据分叉”。
2. 用视图和入口隔离心智,而不一定用表隔离
用户感受到的是产品形态,不是数据库表。
3. 让轻量事项能平滑升级成正式协同对象
这比在两套系统之间搬运数据顺手得多。
4. 提醒分组要贴近工作语义
同一张表里的记录,也可能需要在入口上分桶。
做相关定制时最容易踩的坑
1. 急着新建待办模型
后面会发现消息、活动、权限、搜索、统计都要重复造。
2. 只复用表,不重做入口体验
那用户就感受不到 To-Do 和 Task 的区别。
3. 不给“轻任务升级”为正式任务的路径
结果个人事项和团队事项割裂。
4. 把所有提醒混成一个桶
个人待办和项目协作会相互淹没。
一句话记忆法
Odoo To-Do 的高明之处,不是新造了一个待办模型,而是把
project.task通过视图、入口和分组包装成更轻的个人工作壳。
理解这一句,就能明白为什么这套实现既省模型成本,又保留了很强的协同延展性。
DISCUSSION
评论区