项目深度

Odoo 项目模板为什么不只是复制一个旧项目:日期平移、字段黑名单、角色分派与任务映射链路讲透

很多人觉得 Odoo 项目模板就是 copy 一份项目数据,但官方源码里还处理了日期平移、模板字段黑名单、项目角色到用户的映射,以及任务复制后的结构延续。本文把这套机制拆开。

项目
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 6 阅读

先说结论

很多实施顾问会把“项目模板”理解成:

  • 找一个做好的项目
  • 复制一份
  • 改下名字就开工

/home/ubuntu/odoo-temp/addons/project/models/project_project.pyproject_task.py 的实现说明,官方想做的远不止“复制记录”。

模板创建项目时,至少同时处理这几件事:

  • 项目日期要不要按今天重新平移
  • 哪些字段可以通过 default_ 上下文覆盖
  • 哪些字段绝不能从模板照搬
  • 项目角色如何在新项目中分派到具体用户
  • 项目内的任务结构怎样跟着复制过去

所以项目模板的本质不是“旧项目复印件”,而是 一个可实例化的项目蓝图


第一层:从模板建项目,不是简单 copy,而是有专门入口

project.project.action_create_from_template() 是核心入口。

这个名字本身就值得注意:

  • 不是让用户随便点“duplicate”
  • 而是明确区分“从模板创建”

这意味着在官方设计里,模板实例化是一种单独业务动作,不等于普通复制。

它会带上:

  • copy_from_template=True
  • copy_from_project_template=True

这些上下文标记就是在告诉底层:

现在不是做一份普通副本,而是在把模板落地成真实项目。


第二层:为什么项目日期会自动平移

action_create_from_template() 里,如果模板同时有:

  • date_start
  • date

那么新项目在没显式传日期时,会:

  • 把新的 date_start 设为今天
  • 再按模板原本的起止间隔,推出新的截止日期

这看起来只是小细节,但其实很重要。

因为项目模板真正复制的,不该是“2025 年 11 月到 12 月”这种历史日期,而应该是:

  • 原有工期结构
  • 但落到新的现实时间轴上

这就是模板思维和普通复制思维最大的区别之一:

  • 普通复制保留原值
  • 模板实例化保留相对关系

第三层:为什么 Odoo 要做 whitelist 和 blacklist

项目模板里有两个方法很关键:

  • _get_template_default_context_whitelist()
  • _get_template_field_blacklist()

目前官方给项目模板开放的默认上下文白名单很克制,比如:

  • allow_milestones

而黑名单至少包含:

  • partner_id

这套设计背后的思路非常成熟:

白名单解决“哪些值允许创建时覆写”

也就是模板可以被实例化时安全替换的字段。

黑名单解决“哪些值绝不能机械继承”

partner_id 就是典型例子。

因为模板只是蓝图,客户通常属于具体项目,而不是模板本身的固有属性。

如果把模板客户直接复印到所有新项目,业务上几乎一定出错。


第四层:为什么项目模板不只复制项目本体,还要复制任务骨架

map_tasks() 和相关复制逻辑里,Odoo 会把旧项目下的顶级任务找出来,再把任务树复制到新项目。

这里有两个很重要的点:

1. 顶级任务先复制,子任务跟着结构复制

这保证了任务树骨架能在新项目里重建。

2. 复制时会保留某些结构语义,而不是盲目生成一堆散卡片

这也是为什么项目模板适合做标准实施包、标准交付流程,而不是只适合做“数据备份副本”。


第五层:角色映射才是模板落地很有价值的一步

action_create_from_template() 里还有一段很实用的逻辑:

  • 如果传入 role_to_users_mapping
  • 就遍历新项目下的任务
  • 找出任务上的 role_ids
  • 把映射到该角色的用户补进 user_ids
  • 最后再把 project.task.role_ids 清掉

这段逻辑说明官方非常清楚模板的真实用途:

模板里保留的是“需要什么角色”,实例化时再决定“由谁来做”。

这比直接在模板里写死负责人高级得多。

因为模板负责人往往不可能长期稳定:

  • 今天这个项目经理离职了
  • 下个地区由另一组交付
  • 同一种模板在不同团队用法不同

角色映射就很好地把“组织能力模板”和“具体人员分配”分开了。


第六层:任务模板为什么也有自己的黑名单与创建入口

project_task.py 里,任务模板同样有:

  • _get_template_default_context_whitelist()
  • _get_template_field_blacklist()
  • action_create_from_template()

任务级默认允许从上下文传的字段很少,比如:

  • parent_id

而黑名单同样包含:

  • partner_id

这说明官方不是只在项目层做模板语义,而是在任务层也维持同样原则:

  • 模板是结构和规则
  • 实例是具体业务对象

所以模板和实例的客户、负责人、时点等信息,不该机械混在一起。


新手最容易误解的 4 件事

1. 以为模板创建项目就是普通 duplicate

不对。官方专门区分了上下文和入口方法。

2. 以为模板日期应该原样复制

不对。源码明显倾向于保留工期相对关系,而不是复制历史日期。

3. 以为模板客户应该照搬

不对。partner_id 被列入黑名单,说明官方在主动阻止这种误用。

4. 以为模板负责人应该写死在模板里

不对。官方更推荐“角色 → 用户”的落地映射。


实战上最该注意什么

1. 做模板二开时,先区分“结构数据”和“实例数据”

不要什么都 copy。先想清哪些是蓝图,哪些是具体项目事实。

2. 如果你要支持多团队复用模板,优先做角色映射,不要写死用户

这会让模板生命周期长很多。

3. 调试模板生成项目异常时,要看上下文标记

copy_from_templatecopy_from_project_template 往往直接决定复制分支走向。


一句话记忆法

Odoo 项目模板不是“复制旧项目”,而是“按规则实例化蓝图,并在落地时重算日期、过滤字段、映射角色与重建任务结构”。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。