Knowledge 的模板系统如果只看界面,很像“用一篇标准文章复制出一篇新文章”。但 knowledge_article.py 里的实现告诉你,它实际上是在做一次模板树实例化,不是单记录复制。
主要参考:
enterprise/knowledge/models/knowledge_article.py
一、模板展开的起点不是 body,而是模板树
源码会先从根模板开始,把 (template, article) 成对压栈,递归展开所有需要自动创建的子模板。这里最关键的判断是 template_child_default_create:并不是每个子模板都无脑复制,只有符合“默认随父模板创建”的节点,才会一起实例化。
这就解释了为什么 Knowledge 模板能做出“套件化文档”:一个母模板下面带任务清单、阶段项、记录索引、子页面,但真正创建文章时可以只生成默认需要的那部分。
二、stage 和 child article 是成套复制的,不只是文本内容
官方实现先建 stage,再建 child article,最后才回填正文。这个顺序很重要,因为 child article 在创建时可能要对齐父模板上的 stage 归属。
也就是说,Knowledge 模板复制的不只是“长得像”的页面,而是一套内容结构。结构先落地,正文后渲染,这比单纯 copy 更稳。
三、最容易被忽略的高级点:ref() 会被重绑到新文章
模板正文里的嵌入视图、文章链接、article index,并不会继续指向原模板自身。源码在 _prepare_template / _render_template 里会把 ref('module.xml_id') 解析后,重绑到本次新建出来的文章 ID。
这一步非常关键。否则模板 A 复制出文章 A1,A1 内部的链接还会跳回原模板,而不是跳到 A1 的子文章。官方通过构建 template_xml_id -> article_id 映射,解决了这个问题。
四、属性继承不是“复制所有字段”,而是有明确分层
实例化时,以下内容会被成批拷贝或回填:
article_properties/article_properties_definitioncover_image_idfull_widthiconorigin_template_id- 渲染后的
body
这说明模板和文章的关系并不是一次性脱钩。origin_template_id 明确保留了“我是从哪个模板生成的”这层血缘关系,后续统计、筛选、按模板追溯都依赖它。
五、容易误解的地方:模板 preview 和实例 body 不是同一个概念
template_preview 看起来也在渲染模板,但它只是用于模板预览。真正创建文章时,会带上目标文章 ID、模板映射关系等上下文重新跑一遍渲染。
所以开发里不要偷懒把 preview 当正式 body。你少了 target article 上下文后,引用、索引、嵌入块都可能不对。
六、实战建议
Knowledge 模板相关二开,最推荐遵守三条:
- 先处理模板树和结构,再处理正文 HTML
- 不要跳过
ref()重绑定逻辑 - 把
origin_template_id当成追踪与演进的重要字段保留
Knowledge 模板之所以好用,不是因为它“复制得快”,而是因为它把结构、引用、属性和内容一起安全地迁移到了新文章上。
DISCUSSION
评论区