先说结论
ir.actions.actions.path 不是普通字段。
它会直接进入 URL 空间,所以 Odoo 对它的要求很严格:
- 必须符合特定格式
- 不能使用保留前缀
- 不能在所有继承动作表之间重复
也就是说,这不是“同表唯一”这么简单,而是“跨表全局唯一”。
为什么单表 unique 不够
源码里已经说明了原因:
ir.actions.act_window、ir.actions.act_report_xml、ir.actions.act_url、ir.actions.server、ir.actions.client 都继承自同一个基础表。
PostgreSQL 的继承表机制有个坑:
- 单表 unique 只能约束当前表
- 不能天然约束所有子表之间的全局唯一性
所以 Odoo 不能只靠数据库索引,还要自己补一层手工检查。
Odoo 具体检查了什么
_check_path() 里会检查几件事:
- 只能包含小写字母、数字、下划线和短横线
- 必须以字母开头
- 不能以
m-开头 - 不能以
action-开头 - 不能等于
new - 不能在所有 action 记录里重复
这套规则的目标不是“限制你写字串”,而是为了防止路由、菜单和 action 名称空间互相撞车。
为什么要禁止这些前缀
这些前缀是保留的。
它们通常和内部路由、前端 action 命名或特殊入口有关。留给系统自己用,能减少未来兼容性问题。
如果用户自定义 action 也抢这些名字,短期看好像能用,长期就容易踩升级坑。
为什么要跨表 search_count
Odoo 在约束里会对整个 ir.actions.actions 做一次 search_count。
原因很直接:
- 不能只看当前表
- 要看所有继承到这个基础表的动作
- 否则不同类型动作可能会拿到同一个 path
这就是“数据库索引能拦一部分,业务层再补一层”的典型例子。
实战里怎么避免冲突
如果你要自定义 action path,可以记住三条:
- 用自然、可读、短一点的英文小写路径
- 不要碰保留前缀
- 改完后最好顺手查一下是否已有同名 path
最容易误解的点
很多人以为 path 只是一个“好看的 URL 尾巴”。
其实它已经进入了 Odoo 的动作命名空间,所以必须像对待外部 API 路由一样认真。
一句话总结
Odoo 把 action path 当成全局命名资源来管理:不仅要合规、避开保留前缀,还要跨所有继承的动作表保持唯一。
DISCUSSION
评论区