先说结论
记录上的主附件,不是“最后上传哪个就显示哪个”。
Odoo 会把主附件当成这条记录最值得被代表和展示的文件,因此它会过滤一部分类型,并按 PDF → 图片 → 其他的优先级挑选。
如果你拿“上传顺序”去理解,就会经常误判系统行为。
一、主附件是在哪一步被设置的
mail.thread.main.attachment 在 _message_post_after_hook() 里接管了这个逻辑。
也就是说,很多情况下主附件并不是后台手工单独维护,而是在消息发布后,根据这次消息携带的附件自动决定。
这层设计很重要,因为它把“附件归档”和“协作消息”连在了一起:
- 你发了一条带附件的消息
- 记录不仅多了消息历史
- 还可能顺手更新主附件
二、为什么要过滤 XML 和 octet-stream
源码里有个很实用的细节:默认会过滤掉 XML 和 application/octet-stream。
原因很朴素:
- XML 常常是交换文件、系统数据、电子单据载体
- octet-stream 往往只是浏览器或上传端没识别出具体类型
这些文件未必适合当成“记录封面”或“主预览对象”。
所以 Odoo 的态度是:
能作为业务交换文件存在,不等于适合作为主附件展示。
三、为什么 PDF 优先于图片
源码里 max() 的排序优先级很明确:
- PDF 优先
- 然后图片
- 再然后其他类型
这不是技术癖好,而是非常接近办公现实。
在很多业务记录里,最能代表当前阶段的文件往往是:
- 报价单 PDF
- 合同 PDF
- 报告 PDF
图片虽然也常用,但通常更像补充说明,而不是正式结果物。
四、为什么它不一定会覆盖已有主附件
_message_set_main_attachment_id() 默认不是无脑覆盖。
只有在:
- 当前没有主附件,或者
- 明确
force=True
时才会替换。
这说明 Odoo 不是想让主附件随着每次沟通不停抖动,而是尽量保持一个相对稳定的代表文件。
五、这套机制适合什么,不适合什么
适合:
- 报价、合同、审批单、说明书等正式文件驱动的协作对象
- 需要在界面上快速看到“当前代表文件”的场景
不适合:
- 你想把主附件严格定义为“最后上传文件”
- 你上传的大量文件类型都不是 PDF/图片,却希望系统自动懂你的业务偏好
这类场景通常需要自定义。
六、最常见误区
误区 1:主附件就是最新附件
不是。它更像“最佳代表文件”。
误区 2:只要上传了文件,主附件一定会变
不是。已有主附件默认不会轻易被覆盖。
误区 3:所有可下载附件都适合当主附件
不是。源码已经明确过滤了部分类型。
七、排错顺序
当用户问“为什么这个文件没当上主附件”时,建议这样查:
- 这次文件是不是通过消息附件进入的
- 文件 MIME type 是什么
- 当前记录是否已经有主附件
- 这次调用有没有
force=True - 若有多个附件,谁在 PDF/图片优先级里更靠前
最后一句
主附件表达的是‘这条记录最应该被先看到的文件’,而不是‘最近发生过一次上传’。
只要用这个视角看,它的优先级设计就很合理。
DISCUSSION
评论区