先说结论
在 Odoo 里,一个官网页面不是“有个 URL 的 HTML 片段”这么简单。
从 /home/ubuntu/odoo-temp/addons/website/models/website_page.py 与 website_page_properties.py 看,页面背后至少同时挂着这几件事:
- 页面视图本体
ir.ui.view; - 对外 URL;
- 发布状态和定时发布时间;
- 是否加入菜单;
- 是否作为首页;
- 是否允许搜索引擎索引;
- 改 URL 后要不要给旧地址留跳转。
所以更准确的理解应该是:
Odoo 的页面管理,本质上是在管“一个公开入口的生命周期”,不是只管一段内容。
这也是为什么很多团队改版官网时,真正容易出错的不是编辑内容,而是:
- 页面看起来发布了,前台却不出现;
- 菜单改了但首页没跟着变;
- URL 改完旧外链全断;
- 页面还没到发布时间就提前暴露或完全找不到。
一、website.page 为什么不是普通内容表,而是 ir.ui.view 的外壳
website.page 用了 _inherits = {'ir.ui.view': 'view_id'}。
这说明页面本体并不是一个独立的 HTML blob,而是建立在 QWeb 视图之上的“网站页外壳”。
这样做的好处很明确:
- 页面天然吃到视图体系的继承、渲染和缓存能力;
- 页面仍然可以保留网站层面的 URL、菜单、发布时间等额外治理属性;
- 删除页面时,系统还能判断这个 view 是否只被这一页使用,再决定是否连带清理。
所以页面删除逻辑里会专门处理:
- 如果这个 view 只属于当前页面且没有继承子项,就一起删;
- 否则不能粗暴清理。
这不是小题大做,而是在避免把视图层公共资产顺手删掉。
二、页面“可见”为什么不只看 website_published
很多人以为页面可见性只取决于一个布尔值。
但 website.page 的 _compute_visible() 很明确:
- 要先
website_published = True; - 还要满足
date_publish为空,或者已经早于当前时间。
也就是说,前台可见性的本质是:
发布开关 + 发布时间窗口。
这套设计很适合官网改版、专题页预热、活动页预约上线。
但它也带来一个很常见的误区:
- 后台已经勾了 Published,
- 结果前台还看不到。
很多时候不是缓存问题,而是 date_publish 还没到。
如果你把“是否已发布”和“何时允许曝光”拆开理解,很多看似诡异的现象就很容易解释。
三、为什么页面 URL 变化时,菜单与首页都要一起跟着考虑
website.page.write() 在处理 URL 时,不只是改页面自己的 url 字段。
源码还会做三件事:
- 把新 URL 做 slugify;
- 通过
get_unique_path()确保路径不冲突; - 同步更新相关菜单的 URL;
- 如果这个页面原来就是首页,还要连带处理
homepage_url。
这里很能体现 Odoo 的页面观:
- 页面 URL 不是一个孤立字段;
- 它牵着菜单、首页映射和缓存。
所以页面换链接,本质上是在改一个公开入口的地址主键。系统必须把围绕它的一圈引用也一起整理。
四、为什么“是否加入菜单”会被做成属性,而不是让你自己手工建 menu
website.page.properties.base 里有一组很实用的逻辑:
menu_idsis_in_menu_inverse_is_in_menu()
这套代码的意思很简单:
- 你在页面属性里勾“加入菜单”,系统会自动创建对应
website.menu; - 取消勾选时,系统会按
page_id或 URL 域去找相关菜单并删除。
而且源码还特别注意了 URL 变更场景:
- 对
website.page,优先按page_id找菜单; - 对其它对象,再退回按 URL 找。
这个细节很重要。
因为 URL 是会变的,而 page_id 更稳定。Odoo 明显是在避免“页面改过地址以后,原菜单挂不上或删不掉”的老问题。
五、为什么首页切换不是简单把 URL 改成 /
很多 CMS 把首页理解成“那一页的地址是 /”。
Odoo 不是。
在 website.page.properties 里,首页的核心是网站记录上的:
website.homepage_url
这意味着首页不是页面自己自称“我是 /”,而是:
网站把哪个 URL 认作自己的首页入口。
这样设计有两个明显好处:
- 页面本身可以保留自己的真实 URL;
- 首页映射可以换,而不必强行让某个页面只能叫
/。
这对后续改版非常友好。
比如你想把首页从旧专题切到新 landing page,不一定要重建页面结构,只要调整 homepage 入口映射即可。
六、为什么页面属性里会顺手带上索引控制与旧链接回收
website.page.properties 不只是编辑标题和 URL。
它还带着:
website_indexedredirect_old_urlredirect_type
这说明在 Odoo 的产品观里,页面编辑和 SEO/流量治理是连在一起的。
1. website_indexed
这是在控制:
- 页面是否应该进入可被网站搜索和索引的范围。
它不是是否公开可见的唯一条件,但会影响搜索结果和站内可发现性。
2. redirect_old_url
当你修改页面 URL 时,如果勾选这个选项,write() 结束后会自动创建一条 website.rewrite 记录,把旧地址导向新地址。
这件事非常关键,因为官网改版时最贵的不是换文案,而是外链资产丢失:
- 搜索引擎已收录旧地址;
- 用户书签还在旧地址;
- 外部文章、公众号、邮件都可能引用旧地址。
如果改链接时不顺手回收旧 URL,很多流量会被你自己切断。
七、为什么页面搜索、缓存和多网站特异性都和这套属性强绑定
website.page 里有几个容易被忽略但很要命的点。
1. 多网站下要找“最具体的页面”
_get_most_specific_pages() 会优先处理更具体的网站版本,避免共享页和站点特定页混淆。
2. URL / 可见性 / group 改动会清模板缓存
只要 URL、visibility 或 group_ids 变了,源码会 clear_cache('templates')。
这说明页面访问结果明显依赖这些条件,不能继续吃旧模板缓存。
3. 搜索时也会再补一层权限和公开性边界
前台搜索页面时,非设计器用户只能看到:
- 已发布;
- 可索引;
- visibility 不拦截;
- group 权限允许。
所以“页面存在于库里”与“用户能搜索到它”本来就不是一回事。
八、最容易踩的坑,不是写页面,而是把页面当静态文件看
实施里最常见的几个误区:
误区一:Published 就一定能访问
不对,还要看 date_publish 与 visibility。
误区二:改了 URL,菜单自然会好
不一定。虽然 Odoo 会尽量同步,但你的自定义菜单、手写链接、站外入口不一定自动修好。
误区三:旧 URL 断了是正常的
这在运营上通常是坏实践。大多数正式改版,都应该至少评估是否启用旧地址重定向。
误区四:首页就是 /
在 Odoo 里,首页更像“网站默认入口映射”,不是页面自身身份。
九、实战里怎么用这套机制更稳
如果你正在做企业官网、专题页或长期内容站,我更建议按下面的方式操作。
1. 把页面视为“带生命周期的入口对象”
不要只盯正文和区块。
2. 上线前明确区分三件事
- 是否发布;
- 何时发布;
- 是否可被索引。
这三个不是一件事。
3. 改 URL 时默认评估旧地址回收
特别是已有 SEO 积累或外部传播的页面。
4. 菜单是否展示,尽量走页面属性,不要全靠手工散配
这样后续迁移时一致性更高。
5. 首页切换优先理解为入口映射切换
别一上来就去重构整棵页面树。
结语
Odoo 官网页面的真正复杂度,不在“页面编辑器好不好用”,而在:
系统把页面当成一个长期公开入口来治理。
它要管内容、URL、时间、菜单、首页、索引和旧链接资产。
所以读懂 website.page 与 website.page.properties 之后,你会发现 Odoo 页面管理最有价值的地方,不是拖拖拽拽,而是它已经默认把很多官网运营里最容易被忽视的“发布后问题”放进了模型层。
DISCUSSION
评论区