先说结论
很多人提到 Odoo 邮件进线,首先想到的是:
- 邮件发到某个地址
- 系统自动建单或挂到某个对象上
但从开发视角看,更值得弄清的是:
业务模型到底怎样和
mail.alias建立一对一或可选绑定关系。
这正是 mail.alias.mixin 和 mail.alias.mixin.optional 要解决的问题。
它们不是“收邮件功能按钮”,而是:
- 帮模型管理 alias 生命周期
- 把 alias 字段与业务记录字段拆分处理
- 在 create / write / unlink 时自动保持一致性
mail.alias.mixin 解决的是什么问题
在 /home/ubuntu/odoo-temp/addons/mail/models/mail_alias_mixin.py 里,官方注释已经说得很清楚:
- 这个 mixin 用于让业务模型继承
mail.alias - 目标是形成 模型记录与 alias 的一对一关系
它通过:
_inherits = {'mail.alias': 'alias_id'}
把 mail.alias 挂到业务模型上。
而且 alias_id 是必填的。
这意味着:
只要你用了这个重型 mixin,alias 就不是一个“可有可无的附属信息”,而是该记录结构的一部分。
为什么官方还提供一个 optional 版本
很多场景下,alias 并不是每条记录都必须有。
比如:
- 有的 team 要收邮件
- 有的 team 不需要
- 有的记录先创建,后面才决定是否开放进线
所以官方在 /home/ubuntu/odoo-temp/addons/mail/models/mail_alias_mixin_optional.py 又提供了一个轻量版本。
它的注释直接写明:
- 字段不是强制的
- alias 会根据
alias_name等输入动态创建 - 目的是避免产生大量“空别名”记录
这就是两者最核心的设计分野:
mail.alias.mixin
- 强绑定
- alias 是模型结构一部分
- 没 alias 不行
mail.alias.mixin.optional
- 弱绑定
- alias 按需出现
- 更适合渐进式开通邮件入口
create 时到底发生了什么
optional 版本的 create() 很值得读。
它大致分成四步:
1. 先判断哪些记录需要新 alias
如果:
- 没传
alias_id - 同时又给了
alias_name
就认为这条记录需要新建 alias。
2. 先把 alias 相关字段从业务字段里拆出去
源码里的 _alias_filter_fields() 会把:
- 属于
mail.alias的值 - 和属于业务模型本身的值
拆成两份。
这一步很重要,因为它说明:
create 时不是“所有字段一起写进去”,而是 alias 和业务记录分层处理。
3. 用 sudo() 批量创建 alias
源码明确对 mail.alias 侧使用 sudo().create()。
原因也不难理解:
- 邮件网关入口往往是系统级资源
- 普通业务用户未必有权直接管理 alias 对象
4. 创建完业务记录后,再回写依赖 record 自身的信息
例如 _alias_get_creation_values() 里会带:
alias_parent_thread_id = self.idalias_parent_model_id = ir.model对应 id
也就是说,有些 alias 值只有在业务记录已经真正创建后,才能完整补齐。
write 为什么也这么复杂
write() 不是简单的字段更新,而是在处理两类对象同步。
官方源码里有几个关键动作:
1. 如果原来没 alias,但这次写入了 alias_name
那就现场补建 alias。
这说明 optional 版本支持“先有业务记录,后开启邮件入口”。
2. alias 字段和业务字段继续拆开写
- 业务字段正常
super().write() - alias 字段走
self.mapped('alias_id').sudo().write(alias_vals)
3. 如果公司环境变了,还要同步 alias domain
源码里专门会根据公司去取 alias domain,再把 alias_domain_id 写回去。
这非常重要,因为邮件地址不是只有 alias_name,还包含:
- 哪个域名
- 哪家公司语境
所以它处理的不是“邮箱昵称”,而是完整邮件入口身份。
unlink 为什么还要顺手删 alias
optional 版本在 unlink() 里会:
- 先记住
aliases = self.mapped('alias_id') - 删除业务记录
- 再
aliases.sudo().unlink()
这意味着别名生命周期不是独立悬空的。
如果业务对象没了,对应 alias 也应当一起清掉,避免:
- 残留入口
- 邮件继续打到无效对象
- 系统里越积越多孤儿 alias
这类 mixin 真正常见的误解
误解 1:mail alias 只是一个展示字段
不是。
它背后有自己的模型、权限、域名、父对象绑定和删除链。
误解 2:给模型加个 alias_name 就等于支持邮件进线
不够。
真正难的是:
- 何时建 alias
- 谁来建
- 建完怎么回写
- 改公司后域名怎么同步
- 记录删掉后 alias 怎么收口
误解 3:alias 和业务记录可以随便分开维护
理论上能手改,但官方 mixin 的整个设计方向,其实就是尽量避免两边状态漂移。
开发时该怎么选
适合 mail.alias.mixin
- 每条记录都应该天然拥有邮件入口
- alias 是对象结构的一部分
- 你明确要强约束一对一关系
适合 mail.alias.mixin.optional
- 只有部分记录需要 alias
- 你想允许后期开通
- 你不想因为空 alias 造成数据噪声
一句话收尾
mail.alias.mixin系列真正解决的不是“收邮件”,而是“业务记录与邮件入口对象怎样在创建、修改、删掉时保持生命周期一致”。
DISCUSSION
评论区