django CMS 组合架构解析:内容对象、插件与 Apphook 三大积木如何构成一个站点
CMS后端【免费下载链接】django-cmsThe easy-to-use and developer-friendly enterprise CMS powered by Django项目地址https://gitcode.com/gh_mirrors/dj/django-cms点击查看免费下载django CMS 站点由三类构建块拼装而成内容对象content object、插件plugin与Apphook。本文以 docs/explanation/composition.rst 为主线系统讲解三者各自的职责边界、它们之间的组合规则并给出开发者最常遇到的插件还是 Apphook决策指南同时结合本仓库源码cms/下的模型、插件基类、Apphook 池与 URL 解析器印证底层实现。读完你将能根据内容该放在哪里这一核心问题在站点架构层面做出正确的技术选型。三大构建块各自的职责把三者放在一起对比职责划分一目了然构建块它是什么它拥有什么内容对象Content object持有可编辑内容、向编辑者暴露占位符placeholder的东西。页面Page是最常见也最强大的一种——它存在于页面树中、携带 URL、驱动菜单、承载权限。其他种类还包括别名Alias通过 Alias 插件嵌入的可复用内容以及由附加应用定义的内容对象如博客文章、商品目录条目它的占位符、它的翻译版本以及对于页面而言它的 slug、模板和它在页面树中的位置插件Plugin编辑者可以拖入占位符的可复用内容组件编辑者通过其模型填入的数据以及渲染它的模板Apphook把一个 Django 应用挂载到页面树上的标准方式。应用的 URL以及它定义的内容对象以锚定它的 CMS 页面路径为前缀所挂载页面及其下层的全部 URL一句话概括三者关系内容对象是可编辑内容的单元插件是它内部的组合单元Apphook 是其他内容加入页面树的方式。从源码看这个职责划分落实在模型层非常具体。Page模型cms/models/pagemodel.py持有的是site、parent、is_home、login_required、application_urls即 Apphook 绑定等长期身份字段而标题、slug、模板、占位符、in_navigation、soft_root、meta_description等可编辑状态全部放在PageContent模型上cms/models/contentmodels.py。Page的 docstring 说得非常直白APageis an abstract entity. It does not have any content associated with it, nor does it provide any slugs to build a URL.——页面是抽象实体内容与 URL 分别由PageContent与PageUrl承担。这种grouper分组器content内容的拆分是 django CMS 内容对象体系的底层模式详见 内容对象与 grouper/content 模式grouper 保存长期身份一件事一行记录content 保存按语言 × 版本切分的可编辑状态一件事多行记录。一个同时存在英文和德文的页面在数据库里就是1 行Page加2 行PageContent每种语言一行装上djangocms-versioning后每种语言还会按草稿/已发布/未发布/归档版本继续膨胀出更多行。三者如何拼装在一起只用文字分别描述三者很容易混淆把它们放进一张组合图里就清楚了页面树 │ └── 页面 内容对象拥有占位符存在于树中 │ ├── 占位符 由页面模板声明 │ ├── 插件 │ │ └── 插件嵌套若父插件允许 │ └── 插件 │ └── Apphook 可选——在此 URL 挂载一个 Django 应用 │ └── 应用定义的内容对象 │ 例如博客文章、商品、投票 └── 占位符 └── 插件 页面树之外 别名Alias 通过 Alias 插件嵌入的可复用内容对象 └── 占位符 └── 插件支配这张图的几条规则内容对象拥有占位符占位符容纳插件插件之间可以嵌套组合。这条规则对所有内容对象一视同仁——页面、别名、应用自定义对象都一样。页面树是页面的 URL 来源让页面出现在菜单中并承载按页面的权限模型。非页面内容对象要获得 URL要么通过 Apphook 挂到树上要么自己实现get_absolute_url()。插件只活在占位符里从不单独存在。即使同一个插件出现在许多内容对象上每一次出现都是一个独立的插件实例拥有各自的配置。父插件声明allow_children True后插件可以包含其他插件。行、列、手风琴这类复合布局就是这样搭出来的。源码层面cms/plugin_base.py 中allow_children默认False开启后插件模板可通过instance.child_plugin_instances拿到全部子插件实例再用{% render_plugin %}模板标签逐个渲染child_plugin_instances在 cms/models/pluginmodel.py 定义子插件被预取并可直接使用。相关的child_classes、parent_classes、require_parent属性则进一步约束谁可以嵌进谁。没有页面可挂的 Apphook 毫无意义。它不是一项设置而是在 CMS 页面的高级设置Advanced settings中建立的绑定页面提供 URL 前缀应用提供前缀之下的一切。Apphook 的绑定在模型上就是Page的application_urls字段cms/models/pagemodel.py配合application_namespace记录实例命名空间。运行时cms/appresolver.py 的_get_app_patterns()会扫描所有设置了 Apphook 的页面把应用 urlconf 里的每个 pattern 递归改写recurse_patterns成页面路径 原 pattern的前缀形式再构建成AppRegexURLResolver并入 CMS 的 URLconf——这正是像在urls.py里 include 一个 URLconf只是基础路径由编辑者通过页面树决定的实现机制。插件还是 Apphook决策指南换个角度看问题实质是这份内容到底住在哪里如果它能塞进别人页面上的某个现有占位符里它就是插件如果它是自己的一类事物——拥有自己的列表视图、详情视图和 URL——它就是一个应用通过 Apphook 挂载。你的诉求是…选它为什么一个可复用的内容组件编辑者能拖进任意占位符插件插件是占位符内部的组合单元一个完整的子应用博客、商品目录、投票、搜索带列表和详情视图ApphookApphook 拥有 URL 前缀并自带内容对象一个完全由编辑者拼装内容的页面页面 插件页面内容对象的默认流程一个主体由 Django 视图驱动的页面列表、详情、搜索结果页面 Apphook页面提供 URL应用提供视图和如果有自己的内容对象编辑者要挑选哪些记录显示在页面内嵌的列表里插件模型引用你的记录组件住在页面上只有数据住在你应用的模型里编辑者要能通过移动页面把子站点挪到不同 URLApphook视图和内容对象是你的URL 属于页面首页放即将到来的活动预告同时/events/有完整活动子站两者都要——预告用插件子站用 Apphook常见组合插件渲染小摘要Apphook 拥有/events/及其下层从源码看插件与 Apphook 的边界插件侧的边界在 cms/plugin_base.py 一目了然CMSPluginBase继承自 Django 的admin.ModelAdmin因此exclude、fields、fieldsets、form、inlines、readonly_fields等 ModelAdmin 选项对插件同样有效而list_display、list_filter、search_fields、actions、ordering等仅服务于 changelist 的选项在 CMS 插件中无效。插件render()方法默认只注入instance与placeholder到上下文加上render_template即构成完整的模型-视图-模板闭环。Apphook 侧的边界在 cms/app_base.pyCMSApp基类通过_urlsurlconf 列表、_menus菜单类列表、name必填的人类可读名称、app_nameDjango namespace、app_config可选配置模型等属性声明一个可挂载应用。注册由 cms/apphook_pool.py 的discover_apps()完成——默认autodiscover_modules(cms_apps)自动发现各应用的cms_apps.py也可通过CMS_APPHOOKS设置显式指定类路径。未继承CMSApp的类会在注册时被ImproperlyConfigured拒绝。当边界变得模糊几个易踩坑的真实场景以下场景常让人困惑但它们都没有破坏上述模型只是看起来像边界情况。插件需要自己的详情 URL产品卡片插件要链接到/products/42/它依然是插件它活在占位符里但详情 URL 必须来自某个地方。惯用答案是把详情视图放进挂载于 CMS 页面的 Apphook这样 URL 由编辑者控制。如果改在项目根urls.py里接视图URL 也能工作但被写死在代码里编辑者无法控制。一页上有许多同一个插件每一个都是独立的插件实例各自携带配置。它们共享同一个模型和模板但不共享数据——这正是插件作为占位符内组合单元的体现。一个页面同时有占位符和 Apphook页面自身 URL/events/正常渲染它的占位符URL之下/events/2026-summit/则交给 Apphook。Apphook 页面下的子 CMS 页面无法可靠访问——因为 Apphook 拥有那段 URL 空间请求可能被解析到应用而不是子页面子页面甚至完全不可达。这一点在 cms/appresolver.py 的applications_page_check()中也有印证应用解析器对路径的优先级高于普通 CMS 页面。这与 Apphook 文档 中Apphook 会吞掉页面之下全部 URL的警告一致。同一个 Django 应用挂在多个页面上每个挂载点是独立的。如果编辑者希望每个挂载点配置不同不同分类、不同 feed应用需要提供Apphook 配置apphook configuration机制。配置体系涉及三个容易混淆的概念Apphook 类开发者定义连接页面与应用 URLconf、Apphook 配置类开发者定义描述编辑者能选什么、Apphook 配置实例编辑者创建为某个挂载点选定具体配置数据。源码中CMSApp的get_configs()、get_config(namespace)、get_config_add_url()就是配置化 Apphook 必须实现的三个方法cms/app_base.py而__new__中通过app_config.cmsapp强制一个 Apphook 配置类只能绑定一个 Apphook。不复制内容地跨页面复用这正是**别名Alias**的用途一个活在页面树之外的内容对象通过 Alias 插件嵌入到占位符中。页脚、促销横幅、当前营业时间条都是典型用例。别名的数据模型同样遵循 grouper/content 拆分——Alias是 grouperAliasContent是 content——因此也能像页面一样参与翻译与版本化。组合模型与发布、菜单的联动理解组合关系后两条联动规则值得单独强调发布状态是沿内容对象传播的。Apphook 附着在页面上因此继承页面的发布状态页面未发布时 Apphook 不对外服务取消发布页面也会让 Apphook 下线同时路径上的父页面也必须已发布。核心 CMS 本身没有独立的发布动作——没有版本化包时编辑即发布——装上djangocms-versioning后每个内容行才获得草稿/已发布/未发布/归档四种状态而PageContent.objects默认管理器此时只返回每种语言下已发布的那一行admin_manager则是返回全部行的逃生门详见 发布模型 与 内容对象。未发布的页面不仅隐藏其插件也会把它的 Apphook 一起带下线。菜单由页面、Apphook 与自定义菜单代码共同产出。页面通过cms.cms_menus.CMSMenu生成器进入菜单节点列表NavExtender把 Apphook 应用的菜单并进来SoftRootCutter负责按软根soft root裁掉多余的深层菜单项详见 菜单系统工作原理。由于 Apphook 挂载的页面拥有真实 URL其应用页面可以正常参与导航这是在urls.py里直接 include做不到的。下一步阅读插件详解——插件到底是什么、由哪些文件组成、插件模型该如何设计以及djangocms-frontend提供的模板组件与自定义组件两种轻量路径。Apphook 详解——Apphook 是什么、它如何改变 URL 处理以及 Apphook 配置configuration机制。Apphook 实战指南——Apphook 挂载与配置的完整操作步骤。内容对象与 grouper/content 模式——每个内容对象遵循的底层拆分模式。发布模型——发布状态如何作用于页面上每个内容对象以及版本化包的接入契约。菜单系统工作原理——页面、Apphook 与自定义菜单代码如何组合成编辑者看到的导航。赞分享CMS后端【免费下载链接】django-cmsThe easy-to-use and developer-friendly enterprise CMS powered by Django项目地址https://gitcode.com/gh_mirrors/dj/django-cms点击查看免费下载相关推荐django CMS多站点管理共享内容如何独立部署多个品牌站点django CMS多站点管理共享内容如何独立部署多个品牌站点 在运营矩阵式品牌、集团官网或地区分站时django CMS 的多站点multi siteCMS后端django-cms 3.0.3 升级要点Alias 插件、上下文菜单扩展 API 与 Apphook 权限继承django cms 3.0.3 升级要点Alias 插件、上下文菜单扩展 API 与 Apphook 权限继承 本篇升级指南围绕 django cms 3.CMS后端ArchiveBox Chrome 插件整合设计解析一个插件一个 ArchiveResult 的插件架构ArchiveBox Chrome 插件整合设计解析一个插件一个 ArchiveResult 的插件架构 本指南围绕 ArchiveBox 仓库中的设计文档后端数据工程上一篇洛雪音乐播放异常技术解决方案从诊断到自愈的完整指南下一篇5分钟搞定Windows平台PDF处理效率工具全攻略创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考