Wagtail 5.2 LTS 版本全解析:图片性能优化、OpenSearch 支持、ModelViewSet 增强与升级指南
【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail
Wagtail 5.2 于 2023 年 11 月 1 日发布,并被正式指定为Long Term Support(LTS)长期支持版本——在下一个 LTS 版本到来之前(通常为期 12 个月),该系列会持续接收针对安全问题和数据丢失问题所需的维护更新。本篇文章以 Wagtail 5.2 官方发布说明 为主线,结合当前仓库源码,系统梳理 5.2 的核心新特性(重设计的页面列表、OpenSearch 支持、picture/srcset_image响应式图片标签、Stimulus 客户端扩展、ModelViewSet能力迁移)、逐条展开升级注意事项,并给出可落地的迁移示例。读完本文,你将掌握 5.2 的新能力用法、底层实现原理,以及从旧版本平滑升级所需的全部改动清单。
版本定位:LTS 与维护周期
Wagtail 5.2 被指定为 Long Term Support(LTS)版本。根据发布说明,LTS 版本会在下一个 LTS 版本发布之前(通常是 12 个月)持续收到针对安全问题和数据丢失问题的维护更新。对于追求稳定、不愿频繁大版本升级的生产项目,5.2 系列是值得长期驻留的版本基线。它同时也是首个正式支持Python 3.12的版本(由 Matt Westcott 贡献),对升级 Python 运行环境的项目有直接意义。
亮点一:重设计的页面列表视图(Page Explorer)
5.2 对后台的页面资源管理器(page explorer)列表视图做了重新设计,以提供更流畅的导航与搜索体验。从官方截图可以看到,新的列表在搜索时呈现"精简"(slimmed-down)的结果行,行内直接展示标题、状态、操作等核心信息,配合新的面包屑与下拉按钮交互,整体信息密度和操作效率都有明显提升。该特性由 Ben Enright、Matt Westcott、Thibaud Colas 与 Sage Abdullah 共同开发。
亮点二:OpenSearch 正式支持
5.2 起,OpenSearch 被正式支持为 Elasticsearch 的替代方案(由 Matt Westcott 开发)。仓库中的对应后端实现位于 wagtail/search/backends/opensearch2.py 与 wagtail/search/backends/opensearch3.py,分别对应 OpenSearch 2.x 与 3.x。
完整的配置细节见 OpenSearch 配置文档,核心要点如下:
- 通过
WAGTAILSEARCH_BACKENDS设置选择后端,可用的后端标识为wagtail.search.backends.opensearch2(OpenSearch 2.x)与wagtail.search.backends.opensearch3(OpenSearch 3.x)。 - 前置依赖是 OpenSearch 服务本身,以及 pip 安装的
opensearch-py包,且包的大版本必须与所连接的 OpenSearch 大版本一致:
pip install "opensearch-py>=2,<3" # 对应 OpenSearch 2.x pip install "opensearch-py>=3,<4" # 对应 OpenSearch 3.x- 一个典型的配置示例:
WAGTAILSEARCH_BACKENDS = { "default": { "BACKEND": "wagtail.search.backends.opensearch3", "URLS": ["http://localhost:9200"], "INDEX": "wagtail", "TIMEOUT": 5, "AUTO_UPDATE": True, "ATOMIC_REBUILD": True, } }- 若使用 OpenSearch 的 demo 配置,其 TLS 证书位于 OpenSearch 配置目录(通常为
/usr/share/opensearch/config/或/etc/opensearch/),客户端证书与密钥分别名为kirk.pem与kirk-key.pem,可据此配置带认证的URLS。 - OpenSearch 后端兼容Amazon OpenSearch Service,但需要通过
requests-aws4auth包处理基于 IAM 的认证,再在WAGTAILSEARCH_BACKENDS中配置相应的认证参数。
亮点三:picture / srcset_image 响应式多格式图片
5.2 为图片模板标签引入两大新成员,目标是显著降低全站图片体积、缩短加载时间并减小环境碳足迹(该特性由 Paarth Agarwal 与 Thibaud Colas 在 Google Summer of Code 项目及 Green Web Foundation、Green Coding Berlin 合作中完成):
picture标签:一次性按多种格式 × 多种尺寸批量生成图片,输出 HTML<picture>标签;srcset_image标签:一次性生成多种尺寸,输出带srcset属性的<img>标签。
例如一次生成 6 个变体(AVIF/WebP/JPEG 三种格式 × 400/800 两种宽度):
{% picture page.photo format-{avif,webp,jpeg} width-{400,800} sizes="80vw" %}输出:
<picture> <source sizes="80vw" srcset="/media/images/pied-wagtail.width-400.avif 400w, /media/images/pied-wagtail.width-800.avif 800w" type="image/avif"> <source sizes="80vw" srcset="/media/images/pied-wagtail.width-400.webp 400w, /media/images/pied-wagtail.width-800.webp 800w" type="image/webp"> <img sizes="80vw" srcset="/media/images/pied-wagtail.width-400.jpg 400w, /media/images/pied-wagtail.width-800.jpg 800w" src="/media/images/pied-wagtail.width-400.jpg" alt="A pied Wagtail" width="400" height="300"> </picture>源码级原理
从源码看,这套机制的核心是Filter类的花括号展开(brace expansion)与Picture/ResponsiveImage对象的 HTML 渲染:
- wagtail/images/models.py 中的
Filter.expand_spec()会把形如width-{100,200}的规格展开为["width-100", "width-200"],多个花括号段之间取笛卡尔积,再以|组合成完整规格串;对应地,srcset_image/picture标签允许的规格字符集扩大到包含{}与,(见 jinja2tags.py 中基于Filter.pipe_expanding_spec_pattern的语法校验)。 ResponsiveImage(wagtail/images/models.py)通过get_width_srcset()生成url 400w, url 800w形式的宽度描述符;单张图片时不输出srcset,并以第一张 rendition 作为兜底<img>。Picture(wagtail/images/models.py)按source_format_order = [avif, webp, jpeg, png, gif]的优先级决定<source>的书写顺序与"兜底格式"(fallback format),浏览器会选取其支持的第一种格式;若只生成了一种格式,则退化为带srcset的普通<img>并包裹在<picture>中。- 格式与质量可通过设置项调节:源码 wagtail/images/models.py 显示 JPEG 质量默认 76(
WAGTAILIMAGES_JPEG_QUALITY)、WebP 默认 80(WAGTAILIMAGES_WEBP_QUALITY)、AVIF 默认 61(WAGTAILIMAGES_AVIF_QUALITY),并支持format-{...}系列操作符与preserve-svg跳过 SVG 转换等细节。
更完整的用法(多格式、响应式尺寸、AVIF 支持、AbstractImage.get_renditions())见 图片主题文档;这两个新标签同样支持在 Jinja 模板中使用,对应 API 见 Jinja2 参考(WagtailImagesExtension在 wagtail/images/jinja2tags.py 中把image、image_url、srcset_image、picture注册为 Jinja 全局函数)。
亮点四:官方支持 Stimulus 客户端扩展
5.2 起,Wagtail 正式支持使用Stimulus进行后台(admin)客户端定制,并新增了专门的开发者文档页 扩展客户端侧(由核心贡献者 LB (Ben) Johnston 编写)。该文档覆盖客户端可扩展性的基础主题:
- 添加自定义 JavaScript;
- 基于 DOM 事件以及 Wagtail 自定义 DOM 事件进行扩展;
- 基于 Stimulus 扩展;
- 基于 React 扩展。
与之配套,5.2 同步将多个既有组件迁移到 Stimulus:表单提交列表的复选框切换迁移到共享的w-bulk控制器、编辑器未保存消息弹窗由共享的w-message控制器驱动、tooltip/dropdown 全面改用data-*-value属性、w-action控制器新增reset方法、Stimulus dialog 新增notifytarget 等。这也解释了下方升级注意事项中大量"旧 data 属性/旧事件名"被替换的现象——后台前端正在稳步走向以 Stimulus 为统一框架的架构。
亮点五:ModelViewSet 能力大幅增强
5.2 将SnippetViewSet的一批能力下沉到通用基类{class}~wagtail.admin.viewsets.model.ModelViewSet,使得开发者**无需把模型注册为 snippet** 也能获得完整的后台管理视图。仓库中ModelViewSet的实现位于 [wagtail/admin/viewsets/model.py](https://link.gitcode.com/i/939380eea044e7e93a6af4c7fc04a7db),其类属性直接印证了 5.2 的新能力,例如add_to_reference_index = True(默认注册引用索引)、inspect_view_enabled = False(默认关闭 inspect 视图)、list_per_page = 20` 等。
本次从SnippetViewSet迁移到ModelViewSet的内容包括:
- 将
SnippetViewSet的菜单注册机制迁移到基类ViewSet; - 将模板覆盖机制、
list_display迁移到ModelViewSet; - 将
list_filter、filterset_class、search_fields、search_backend_name、list_export、export_filename、list_per_page、ordering等属性迁移到ModelViewSet; - 为通用
IndexView/CreateView增加默认头部标题; - 通用
IndexView支持使用过滤器与导出列表; - 为
ModelViewSet新增通用UsageView、InspectView,并从 snippets 中提取通用HistoryView; - 从页面面包屑中提取通用面包屑功能,并为自定义
ModelViewSet视图提供面包屑支持; - 允许
ModelViewSet用于非整数主键模型; - 为
ModelViewSet注册的模型默认开启引用索引(reference index)追踪。
同时,以下新特性也加入了通用后台视图,SnippetViewSet同样可用:
- 允许通过
ModelViewSet覆盖IndexView.export_headings; - 允许在通用
IndexView上定义列表按钮(listing buttons)。
完整用法参见 generic views 文档。
亮点六:管理界面打磨与搜索推广外链
5.2 对后台用户界面做了一批细节打磨:页面状态侧栏的相对日期上以 tooltip 显示完整的首次发布时间、无面板锚点时不再渲染 minimap、dashboard 面板的列表改用下拉按钮、面包屑设计细化、表单提交与简单翻译提交支持 Shift + Click 批量行为、按用户权限优化审计日志过滤等。
另一个实用新特性是:推广搜索结果(Promoted Search Results)条目现在可以使用外部 URL 与自定义链接文本,而不再只能指向 Wagtail 内的页面,便于跨站点管理推广内容(感谢 TopDevPros 与芝加哥大学图书馆的 Brad Busenius)。
其他功能亮点速览
发布说明中的"Other features"包含一批值得关注的能力,择要列出:
- 模板标签防缓存:新增
wagtailcache与wagtailpagecache模板标签,确保预览 Page 或 Snippet 时不会被缓存; - 视图限制随页面复制/别名迁移:复制页面或创建别名时,其视图限制(view restrictions)会一并复制到目标;
- StreamField 值支持 pickle;
- Chooser 增强:
ChooserViewSet支持指定get_object_list方法;chooser widgets 新增linked_fields机制,可按调用页面的字段限制可选范围; - TableBlock 支持合并单元格:通过
mergedCells选项实现; - InlinePanel 焦点与 DOM 事件:在
InlinePanel内新增面板时焦点会移至新内容(与StreamField一致),并新增 ready/新增/移除等InlinePanelDOM 事件; {% component %}标签支持传递额外上下文变量;- API v2 支持多字段排序,
PagesAPIViewSet子类可通过model属性覆盖默认 Page 模型; - Email 链接 chooser 支持主题与正文;
wagtail_update_image_renditions管理命令输出可视化进度条;- 上传文件哈希生成增大读缓冲区,并在 Python 3.11+ 使用
hashlib.file_digest提升效率; Block.get_template可接收value参数,允许按块值选择模板;purge_revisions管理命令现在会尊重带on_delete=PROTECT外键关系的 revision,不再删除它们。
升级注意事项:影响所有项目
MariaDB 上的 UUID 字段与 Django 5.0
Django 5.0 在 MariaDB 10.7 及以上引入了对 MariaDB 原生 UUID 类型的支持,这会破坏旧版本 Django/MariaDB 创建的CHAR型 UUID 的向后兼容性。因此,将站点升级到 Django 5.0+ 与 MariaDB 10.7+ 后,创建或编辑页面时很可能出现类似Data too long for column 'translation_key' at row 1或Data too long for column 'uuid' at row 1的错误。
修复方法是升级后运行(该命令自Wagtail 5.2.5起提供)convert_mariadb_uuids管理命令:
./manage.py convert_mariadb_uuids该命令会把 Wagtail 使用的所有既有 UUID 字段转换为新格式。在 Django 5.0+ 与 MariaDB 10.7+ 下新建的站点不受影响,无需执行。
升级注意事项:旧功能弃用
旧版 moderation 系统弃用
在 Wagtail 2.10 被新工作流(workflow)系统取代的旧版 moderation 系统,如今正式标记为弃用。自 2.10 起提交的页面审核已走新工作流,但 2.10 之前提交、仍滞留在旧队列中的页面仍需通过旧系统批准/拒绝:
- 以超级用户登录后台,可在仪表盘看到"Pages awaiting moderation"区块并操作;
- 也可以编程处理:查询
Revision.objects.filter(submitted_for_moderation=True),对每个 revision 调用revision.approve_moderation()或revision.reject_moderation()。
以下 API 与配置随之弃用,并将在未来版本移除(应替换为新工作流的对应能力):
wagtail.models.Revision.submitted_for_moderationwagtail.models.Revision.submitted_revisionswagtail.models.Revision.approve_moderationwagtail.models.Revision.reject_moderationRevisionMixin.save_revision的submitted_for_moderation参数WAGTAIL_MODERATION_ENABLEDwagtail.admin.userbar.ModeratePageItem、ApproveModerationEditPageItem、RejectModerationEditPageItemwagtail.admin.views.home.PagesForModerationPanelwagtail.admin.views.pages.moderationwagtail.permission_policies.pages.PagePermissionPolicy.revisions_for_moderation
详情可回顾 Wagtail 2.10 发布说明。
升级注意事项:影响 Wagtail 定制代码
classname命名约定
Wagtail 从 4.2 开始推行统一的单数classname(而非复数classnames)约定,5.2 将这一约定扩展到菜单与钩子体系。以下类采用新约定:
admin.menu.MenuItemadmin.ui.sidebar.ActionMenuItemadmin.ui.sidebar.LinkMenuItemadmin.ui.sidebar.PageExplorerMenuItemcontrib.settings.registry.SettingMenuItem
以下钩子在使用classnames生成菜单项时可能受影响:register_admin_menu_item、register_settings_menu_item(钩子完整清单见 hooks 参考)。
旧的classnames关键字仍可用但会触发弃用警告,未来版本将移除。示例(注意第 8 行使用classname=):
from django.urls import reverse from wagtail import hooks from wagtail.admin.menu import MenuItem @hooks.register("register_admin_menu_item") def register_frank_menu_item(): return MenuItem( "Frank", reverse("frank"), icon_name="folder-inverse", order=10000, classname="highlight-menu", # 不是 classnames=... )ModelViewSet 的编辑/删除 URL 变更(支持非整数主键)
为支持非整数主键模型,ModelViewSet中编辑与删除视图的 URL 模式已变更。相对于视图集的url_prefix:
- 编辑 URL 由
<int:pk>/改为edit/<str:pk>/; - 删除 URL 由
<int:pk>/delete/改为delete/<str:pk>/。
若你通过django.urls.reverse配合get_url_name()生成 URL,无需改动;但若在代码中硬编码了这些 URL,必须更新。旧 URL 的重定向仅为向后兼容保留,未来版本会移除。SnippetViewSet中旧 URL 的重定向也已被标记为未来移除。
ModelViewSet 自动注册引用索引
通过ModelViewSet注册的模型默认启用引用索引追踪,不再需要在 app 的ready()方法中手动调用ReferenceIndex.register_model()。若不希望如此,可在ModelViewSet子类上设置add_to_reference_index = False(对应源码 wagtail/admin/viewsets/model.py)。详见 引用索引管理文档。
其他行为变更一览
GroupViewSet的IndexView.results_template_name更名:由wagtailusers/groups/results.html改为wagtailusers/groups/index_results.html,自定义过该模板(如 自定义组视图)的项目需要同步重命名。construct_snippet_listing_buttons钩子不再接受context参数:需要访问视图计算值的场景,应改为覆盖SnippetViewSet.index_view_class自定义IndexView(特别是get_list_buttons与get_list_more_buttons方法)。旧签名会触发警告并收到空字典{}。页面列表/头部按钮钩子的
page_perms参数被user取代:register_page_header_buttons:由func(page, page_perms, next_url)变为func(page, user, next_url, view_name),其中view_name为'edit'或'index';register_page_listing_buttons:变为func(page, user, next_url);construct_page_listing_buttons:变为fn(buttons, page, user, context);register_page_listing_more_buttons:变为func(page, user, next_url);ButtonWithDropdownFromHook构造器同样改传user。
旧代码如需获取权限测试对象,可用
page.permissions_for_user(user)替代。相关钩子说明见 hooks 参考。
升级注意事项:未文档化的内部变更
面包屑类名由单数改为复数
自定义面包屑样式时,类名'w-breadcrumb'已改为'w-breadcrumbs'。
Snippets 模板重构为复用slim_header.html
以下 snippets 头部模板已被移除,绝大多数场景可用wagtailadmin/shared/headers/slim_header.html替代:
wagtailsnippets/snippets/headers/_base_header.htmlcreate_header.html、edit_header.html、history_header.html、list_header.html、usage_header.html
侧栏组合类移除
BaseSidePanels、PageSidePanels、SnippetSidePanels类已移除,每个侧栏现在直接在视图中实例化;BasePreviewSidePanel/PagePreviewSidePanel/SnippetPreviewSidePanel合并为PreviewSidePanel;BaseStatusSidePanel更名为StatusSidePanel。媒体对象可用wagtail.admin.ui.components.MediaContainer组合。
旧写法:
from wagtail.admin.ui.side_panels import PageSidePanels def my_view(request): ... side_panels = PageSidePanels( request, page.get_latest_revision_as_object(), show_schedule_publishing_toggle=False, live_page=page, scheduled_page=page.get_scheduled_revision_as_object(), in_explorer=False, preview_enabled=True, comments_enabled=False, ) return render( request, template_name, {"page": page, "side_panels": side_panels, "media": side_panels.media}, )新写法:
from wagtail.admin.ui.components import MediaContainer from wagtail.admin.ui.side_panels import PageStatusSidePanel, PreviewSidePanel def my_view(request): ... side_panels = [ PageStatusSidePanel( page, request, show_schedule_publishing_toggle=False, live_object=page, scheduled_object=page.get_scheduled_revision_as_object(), locale=page.locale, translations=translations, ), PreviewSidePanel( page, request, preview_url=reverse("wagtailadmin_pages:preview_on_edit", args=[page.id]), ), ] side_panels = MediaContainer(side_panels) return render( request, template_name, {"page": page, "side_panels": side_panels, "media": side_panels.media}, )面包屑迁移到 Stimulus:事件与 data 属性变化
头部面包屑组件已迁移到 Stimulus 控制器,事件与 data 属性均发生变化(自定义头部实现、以及未使用breadcrumbs却依赖展开/收起行为的自定义面包屑可能受影响):
| 旧事件 | 新事件 |
|---|---|
'wagtail:breadcrumbs-expand' | 'w-breadcrumbs:opened' |
'wagtail:breadcrumbs-collapse' | 'w-breadcrumbs:closed' |
| 旧 data 属性 | 新 data 属性 |
|---|---|
data-breadcrumb-next | data-controller="w-breadcrumbs" |
data-toggle-breadcrumbs | data-w-breadcrumbs-target="toggle">window.updateFooterSaveWarning = (formDirty, commentsDirty) => { if (!formDirty && !commentsDirty) { document.dispatchEvent(new CustomEvent('w-unsaved:clear')); } else { const [type] = [ formDirty && commentsDirty && 'all', commentsDirty && 'comments', formDirty && 'edits', ].filter(Boolean); document.dispatchEvent(new CustomEvent('w-unsaved:add', { detail: { type } })); } };其他内部变更速查
结语Wagtail 5.2 作为一个 LTS 版本,交出了相当扎实的成绩单: 【免费下载链接】wagtailA Django content management system focused on flexibility and user experience 创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考 |