docs/releases/5.2.md
November 1, 2023
---
local:
depth: 1
---
Wagtail 5.2 is designated a Long Term Support (LTS) release. Long Term Support releases will continue to receive maintenance updates as necessary to address security and data-loss related issues, up until the next LTS release (typically a period of 12 months).
The page explorer listing view has been redesigned to allow improved navigation and searching. This feature was developed by Ben Enright, Matt Westcott, Thibaud Colas and Sage Abdullah.
OpenSearch is now formally supported as an alternative to Elasticsearch. For configuration details, see OpenSearch configuration. This feature was developed by Matt Westcott.
Wagtail has new template tags to reduce the loading time and environmental footprint of images:
picture tag generates images in multiple formats and-or sizes in one batch, creating an HTML <picture> tag.srcset_image tag generates images in multiple sizes, creating an `` tag with a srcset attribute.As an example, the picture tag allows generating six variants of an image in one go:
{% picture page.photo format-{avif,webp,jpeg} width-{400,800} sizes="80vw" %}
This outputs:
<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">
</picture>
We expect those changes to greatly reduce the weight of images for all Wagtail sites. We encourage all site implementers to consider using them to improve the performance of the sites and reduce their carbon footprint. For further details, For more details, see and . Those new template tags are also supported in Jinja templates, see for the Jinja API.
This feature was developed by Paarth Agarwal and Thibaud Colas as part of the Google Summer of Code program and a partnership with the Green Web Foundation and Green Coding Berlin, with support from Dan Braghiș, Thibaud Colas, Sage Abdullah, Arne Tarara (Green Coding Berlin), and Chris Adams (Green Web Foundation). We also thank Aman Pandey for introducing AVIF support in Wagtail 5.1, Andy Babic for creating AbstractImage.get_renditions() in the same release; and Storm Heg, Mitchel Cabuloy, Coen van der Kamp, Tom Dyson, and Chris Lawton for their feedback on RFC 71.
Wagtail now officially supports client-side admin customizations with Stimulus. The developer documentation has a dedicated page about . This covers fundamental topics of client-side extensibility, such as:
Thank you to core contributor LB (Ben) Johnston for writing this documentation.
ModelViewSet improvementsSeveral features from {class}~wagtail.snippets.views.snippets.SnippetViewSet have been implemented in {class}~wagtail.admin.viewsets.model.ModelViewSet, allowing you to use them without registering your models as snippets. For more details on using ModelViewSet, refer to .
SnippetViewSet menu registration mechanism to base ViewSet class (Sage Abdullah)SnippetViewSet template override mechanism to ModelViewSet (Sage Abdullah)SnippetViewSet.list_display to ModelViewSet (Sage Abdullah)list_filter, filterset_class, search_fields, search_backend_name, list_export, export_filename, list_per_page, and ordering from SnippetViewSet to ModelViewSet (Sage Abdullah, Cynthia Kiser)IndexView and CreateView (Sage Abdullah)IndexView (Sage Abdullah)UsageView to ModelViewSet (Sage Abdullah)InspectView to ModelViewSet (Sage Abdullah)HistoryView from snippets and add it to ModelViewSet (Sage Abdullah)ModelViewSet views (Sage Abdullah)ModelViewSet to be used with models that have non-integer primary keys (Sage Abdullah)ModelViewSet (Sage Abdullah)In addition, the following new features have been added to the generic admin views as part of ModelViewSet, which can also be used with SnippetViewSet.
IndexView.export_headings via ModelViewSet (Christer Jensen, Sage Abdullah)IndexView (Sage Abdullah)Several tweaks have been made to the admin user interface which we hope will make it easier to use.
Promoted search result entries can now use an external URL along with custom link text, instead of linking to a page within Wagtail. This makes it easier to manage promoted content across multiple websites. Thank you to TopDevPros, and Brad Busenius from University of Chicago Library.
wagtailcache and wagtailpagecache template tags to ensure previewing Pages or Snippets will not be cached (Jake Howard)field.html template (Sage Abdullah)wagtail.publish log action on aliases when they are created from live source pages or the source page is published (Dan Braghis)wagtail.unpublish log action on aliases when source page is unpublished (Dan Braghis)get_object_list method on ChooserViewSet (Matt Westcott)linked_fields mechanism on chooser widgets to allow choices to be limited by fields on the calling page (Matt Westcott)TableBlock with the mergedCells option (Gareth Palmer)InlinePanel, focus will now shift to that content similar to StreamField (Faishal Manzar)placement in human_readable_date the tooltip template tag (Rohit Sharma){% component %} tag (Matt Westcott)PagesAPIViewSet override default Page model via the model attribute (Neeraj Yetheendran, Herbert Poul)wagtail_update_image_renditions management command (Faishal Manzar)hashlib.file_digest if available (Python 3.11+) (Jake Howard)Block.get_template to allow varying template based on value (Florian Delizy)InlinePanel DOM events for when ready and when items added or removed (Faishal Manzar)Filter instances as input for AbstractImage.get_renditions() (Thibaud Colas)purge_revisions management command now respects revisions that have an on_delete=PROTECT foreign key relation and won't delete them (Neeraj P Yetheendran, Meghana Reddy, Sage Abdullah, Storm Heg)FieldBlocks correctly set the required and aria-describedby attributes (Storm Heg)PublishMenuItem to more easily support overriding its label via construct_page_action_menu (Sébastien Corbin)non_fields_errors for any custom form validation (Sébastien Corbin)WAGTAIL_EMAIL_MANAGEMENT_ENABLED setting by not showing 'email' if disabled (Omkar Jadhav)ViewSet and ModelViewSet (Sage Abdullah)WAGTAILADMIN_BASE_URL on "Integrating Wagtail into a Django project" page (Shreshth Srivastava)WAGTAILADMIN_RICH_TEXT_EDITORS setting (Charlie Sue)python3-venv on Ubuntu (Brian Mugo)permission kwarg support in Panels (LB (Ben) Johnston)FieldPanel('title') examples to use the recommended TitleFieldPanel('title') panel (Chinedu Ihedioha)As part of our adoption of Stimulus, in addition to the new documentation, we have migrated several existing components to the framework. Thank you to our core contributor LB who oversees this project, and to all contributors who refactored specific components.
w-bulk Stimulus implementation (LB (Ben) Johnston)w-message controller (LB (Ben) Johnston, Hussain Saherwala)data-tippy HTML attribute usage to the Stimulus data-*-value attributes for w-tooltip & w-dropdown (Subhajit Ghosh, LB (Ben) Johnston)reset method to support Stimulus driven dynamic field resets via the w-action controller (Chiemezuo Akujobi)notify target on the Stimulus dialog for dispatching events internally (Chiemezuo Akujobi)WagtailTestUtils.get_soup() method for testing HTML content (Storm Heg, Sage Abdullah)ViewSet subclasses to customize url_prefix and url_namespace logic (Matt Westcott)SnippetViewSet registration code (Sage Abdullah)IndexView.results_template_name to results.html (Sage Abdullah)stubs.js to prevent Storybook from crashing (LB (Ben) Johnston)slim_header.html template (Sage Abdullah)slim_header.html template to reduce code duplication (Sage Abdullah)imghdr (Jake Howard)imghdr with Willow's built-in MIME type detection (Jake Howard)@total_ordering usage with comparison functions implementation (Virag Jain)<script type="text/django-form-template"><-/script> template approach with HTML template elements in InlinePanel and expanding formset (Mansi Gundre, Subhajit Ghosh, LB (Ben) Johnston)ModelViewSet and SnippetViewSet (Sage Abdullah)lru_cache usage (Jake Howard)date_since in get_most_popular inside search_promotions.models.Query (TopDevPros)classname (not classnames) attributes for all MenuItem usage, including deprecation warnings (LB (Ben) Johnston)classname (not classnames) attribute within the wagtail.images.formats.Format instance, including deprecation warnings (LB (Ben) Johnston)context argument of construct_snippet_listing_buttons hook (Sage Abdullah)search.Query & search.QueryDailyHits model, move final set of templates from the admin search module to the search promotions contrib module (LB (Ben) Johnston)Django 5.0 introduces support for MariaDB's native UUID type on MariaDB 10.7 and above. This breaks backwards compatibility with CHAR-based UUIDs created on earlier versions of Django and MariaDB, and so upgrading a site to Django 5.0+ and MariaDB 10.7+ is liable to result in errors such as Data too long for column 'translation_key' at row 1 or Data too long for column 'uuid' at row 1 when creating or editing pages. To fix this, it is necessary to run the convert_mariadb_uuids management command (available as of Wagtail 5.2.5) after upgrading:
./manage.py convert_mariadb_uuids
This will convert all existing UUID fields used by Wagtail to the new format. New sites created under Django 5.0+ and MariaDB 10.7+ are unaffected.
The legacy moderation system, which was replaced by the new workflow system in Wagtail 2.10, is now deprecated. Since Wagtail 2.10, submitting a page for moderation will use the new workflow system. However, the legacy moderation system is still in place for approving and rejecting pages that were submitted for moderation before Wagtail 2.10.
To view all pages that are still in the legacy moderation system backlog, you can sign in as a superuser and see if there is a "Pages awaiting moderation" section in the admin dashboard. You can approve or reject the pages from there. You can also do this programmatically by querying for Revision.objects.filter(submitted_for_moderation=True) and calling revision.approve_moderation() or revision.reject_moderation() on each revision.
The legacy moderation system will be removed in a future release. If you still have pages in the moderation queue that were submitted for moderation before Wagtail 2.10, you should approve or reject them before upgrading. See for more details.
As a result, the following features are now deprecated:
wagtail.models.Revision.submitted_for_moderationwagtail.models.Revision.submitted_revisionswagtail.models.Revision.approve_moderationwagtail.models.Revision.reject_moderationsubmitted_for_moderation argument in {meth}wagtail.models.RevisionMixin.save_revisionWAGTAIL_MODERATION_ENABLEDwagtail.admin.userbar.ModeratePageItemwagtail.admin.userbar.ApproveModerationEditPageItemwagtail.admin.userbar.RejectModerationEditPageItemwagtail.admin.views.home.PagesForModerationPanelwagtail.admin.views.pages.moderationwagtail.permission_policies.pages.PagePermissionPolicy.revisions_for_moderationIf you use any of the above features, remove them or replace them with the equivalent features from the new workflow system. The above features will be removed in a future release.
classname convention for MenuItem related classes and hooksWagtail MenuItem and menu hooks have been updated to use the more consistent naming of classname (singular) instead of classnames (plural), a convention that started in Wagtail 4.2.
The current classnames keyword argument naming will be supported, but will trigger a deprecation warning. Support for this variant will be removed in a future release.
The following classes will adopt this new convention.
admin.menu.MenuItemadmin.ui.sidebar.ActionMenuItemadmin.ui.sidebar.LinkMenuItemadmin.ui.sidebar.PageExplorerMenuItemcontrib.settings.registry.SettingMenuItemThe following hooks usage may be impacted if classnames were used when generating menu items.
register_admin_menu_itemregister_settings_menu_itemfrom 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" # not classnames=...
)
ModelViewSet changed to allow non-integer primary keysTo accommodate models with non-integer primary keys, the URL patterns for the edit and delete views in {class}~wagtail.admin.viewsets.model.ModelViewSet have been changed.
Relative to the viewset's {attr}~wagtail.admin.viewsets.base.ViewSet.url_prefix, the following changes have been made:
<int:pk>/ to edit/<str:pk>/<int:pk>/delete/ to delete/<str:pk>/If you use {func}~django.urls.reverse with {meth}~wagtail.admin.viewsets.base.ViewSet.get_url_name to generate the URLs for these views, no changes are needed. However, if you have hard-coded these URLs in your code, you will need to update them to match the new patterns.
Redirects for the legacy URLs are in place for backwards compatibility, but will be removed in a future release.
The URLs for snippets underwent similar changes in Wagtail 2.14. The redirects for the legacy URLs in {class}~wagtail.snippets.views.snippets.SnippetViewSet have now been marked for removal in a future release.
ModelViewSet automatically registers the model to the reference indexModels that are registered with a ModelViewSet now have reference index tracking enabled by default. This means that you no longer need to call ReferenceIndex.register_model() in your app's ready() method for such models. If this is undesired, you can disable it by setting {attr}~wagtail.admin.viewsets.model.ModelViewSet.add_to_reference_index to False on the ModelViewSet subclass. For more details, see .
IndexView.results_template_name renamed from results.html to index_results.htmlThe IndexView's results_template_name attribute in the GroupViewSet has been renamed from wagtailusers/groups/results.html to wagtailusers/groups/index_results.html for consistency with the other viewsets. If you have customized or extended the template, e.g. for , you will need to rename it to match the new name.
construct_snippet_listing_buttons hook no longer accepts a context argumentThe construct_snippet_listing_buttons hook no longer accepts a context argument. If you have implemented this hook, you will need to remove the context argument from your implementation. If you need to access values computed by the view, you'll need to override the {attr}~wagtail.snippets.views.snippets.SnippetViewSet.index_view_class with a custom IndexView subclass. The get_list_buttons and get_list_more_buttons methods in particular may be overridden to customize the buttons on the listing.
Defining a function for this hook that accepts the context argument will raise a warning, and the function will receive an empty dictionary ({}) as the context. Support for defining the context argument will be completely removed in a future Wagtail release.
page_perms argumentThe arguments passed to the hooks register_page_header_buttons, register_page_listing_buttons, construct_page_listing_buttons and register_page_listing_more_buttons have changed. For all of these hooks, the page_perms argument has been replaced by user; in addition, register_page_header_buttons is now passed a view_name argument, which is either 'edit' or 'index', depending on whether the button is being generated for the page listing or edit view. In summary, the changes are:
register_page_header_buttons: Previously func(page, page_perms, next_url), now func(page, user, next_url, view_name).register_page_listing_buttons: Previously func(page, page_perms, next_url), now func(page, user, next_url).construct_page_listing_buttons: Previously func(buttons, page, page_perms, context), now fn(buttons, page, user, context).register_page_listing_more_buttons: Previously func(page, page_perms, next_url), now func(page, user, next_url).Additionally, the ButtonWithDropdownFromHook constructor, and the resulting hook it creates, should now be passed a user argument instead of page_perms.
Existing code that performs permission checks using page_perms can retrieve the same permission tester object using page.permissions_for_user(user).
Hook functions using the old page_perms signature will continue to work, but this is deprecated and will raise a warning. Support for the old signature will be removed in a future Wagtail release.
If using custom styling for the breadcrumbs, this class has changed from singular to plural for a more intuitive class.
| Old | New |
|---|---|
'w-breadcrumb' | 'w-breadcrumbs' |
slim_header.html templateThe templates for the snippets views have been refactored to reuse the shared slim_header.html template. If you have customized or extended the templates, e.g. for , you will need to update them to match the new structure. As a result, the following templates have been removed:
wagtailsnippets/snippets/headers/_base_header.htmlwagtailsnippets/snippets/headers/create_header.htmlwagtailsnippets/snippets/headers/edit_header.htmlwagtailsnippets/snippets/headers/history_header.htmlwagtailsnippets/snippets/headers/list_header.htmlwagtailsnippets/snippets/headers/usage_header.htmlIn most cases, the usage of those templates can be replaced with the wagtailadmin/shared/headers/slim_header.html template. Refer to the snippets views and templates code for more details.
BaseSidePanels, PageSidePanels and SnippetSidePanels classes are removedThe BaseSidePanels, PageSidePanels and SnippetSidePanels classes that were used to combine the side panels (i.e. status, preview and comments side panels) have been removed. Each side panel is now instantiated directly in the view. The wagtail.admin.ui.components.MediaContainer class can be used to combine the Media objects for the side panels.
The BasePreviewSidePanel, PagePreviewSidePanel and SnippetPreviewSidePanel classes have been replaced with the consolidated PreviewSidePanel class.
The BaseStatusSidePanel class has been renamed to StatusSidePanel.
If you use these classes in your code, you will need to update your code to instantiate the side panels directly in the view.
For example, if you have the following code:
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},
)
Update it to the following:
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},
)
The undocumented JavaScript implementation for the header breadcrumbs component has been migrated to a Stimulus controller and now uses different data attributes.
This may impact custom header implementations that relied on the previous approach, custom breadcrumbs that did not use breadcrumbs and require the expand/collapse behavior may be impacted.
| Old | New |
|---|---|
'wagtail:breadcrumbs-expand' | 'w-breadcrumbs:opened' |
'wagtail:breadcrumbs-collapse' | 'w-breadcrumbs:closed' |
| Old | New |
|---|---|
data-breadcrumb-next | data-controller="w-breadcrumbs" |
data-toggle-breadcrumbs | data-w-breadcrumbs-target="toggle" data-action="w-breadcrumbs#toggle mouseenter->w-breadcrumbs#peek" |
data-breadcrumb-item | data-w-breadcrumbs-target="content" |
Note that the root DOM element also includes a set of additional data attributes to function as the breadcrumbs:
data-controller="w-breadcrumbs"
data-action="keyup.esc@document->w-breadcrumbs#close w-breadcrumbs:open@document->w-breadcrumbs#open w-breadcrumbs:close@document->w-breadcrumbs#close"
data-w-breadcrumbs-close-icon-class="icon-cross"
data-w-breadcrumbs-closed-value="true"
data-w-breadcrumbs-open-icon-class="icon-breadcrumb-expand"
data-w-breadcrumbs-opened-content-class="w-max-w-4xl"
data-w-breadcrumbs-peek-target-value="header"
window.updateFooterSaveWarning global util removedThe undocumented global util window.updateFooterSaveWarning has been removed, this is part of the footer 'unsaved' messages toggling behavior on page forms.
This behavior has now moved to a Stimulus controller and leverages DOM events instead. Calling this function will do nothing and in a future release will throw an error.
You can implement roughly the equivalent functionality with this JavaScript function, however, this will not be guaranteed to work in future releases.
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 } }));
}
};
dropdown template tag argument toggle_tippy_offset renamed to toggle_tooltip_offsetThe naming conventions for tippy related attributes have been updated to align with the generic tooltip naming.
If you are using the undocumented dropdown template tag with the offset arg, this will need to be updated.
| Old | New |
|---|---|
{% dropdown toggle_tippy_offset="[0, -2]" %}...{% enddropdown %} | {% dropdown toggle_tooltip_offset="[0, -2]" %}...{% enddropdown %} |
escapescript template tag and escape_script functions are deprecatedAs of this release, the undocumented coreutils.escape_script util and escapescript template tag will no longer be supported.
This was used to provide a way for HTML template content in IE11, which is no longer supported, and was non-compliant with CSP support.
The current approach will trigger a deprecation warning and will be removed in a future release.
{% load wagtailadmin_tags %}
<script type="text/django-form-template" id="id_{{ formset.prefix }}-EMPTY_FORM_TEMPLATE">
{% escapescript %}
<div>Widget template content</div>
<script src="/js/my-widget.js"></script>
{% endescapescript %}
</script>
Use the HTML template element to avoid content from being parsed by the browser on load.
<template id="id_{{ formset.prefix }}-EMPTY_FORM_TEMPLATE">
<div>Widget template content</div>
<script src="/js/my-widget.js"></script>
</template>
classname convention within the Image Format instanceWhen using wagtail.images.formats.Format, the created instance set the argument for classes to the attribute classnames (plural), this has now changed to classname (singular).
For any custom code that accessed or modified this undocumented attribute, updates will need to be made as follows.
Accessing self.classnames will still work until a future release, simply returning self.classname, but this will raise a deprecation warning.
# image_formats.py
from django.utils.html import format_html
from wagtail.images.formats import Format, register_image_format
class CustomImageFormat(Format):
def image_to_html(self, image, alt_text, extra_attributes=None):
# contrived example - pull out the class and render on outside element
classname = self.classname # not self.classnames
self.classname = "" # not self.classnames
inner_html = super().image_to_html(image, alt_text, extra_attributes)
return format_html("<custom-image class='{}'>{}</custom-image>", classname, inner_html)
custom_format = CustomImageFormat('custom_example', 'Custom example', 'example-image object-fit', 'width-750')
register_image_format(custom_format)
search_garbage_collect management command has been removedIn 5.0 the documentation advised that the search_garbage_collect command used to remove old stored search queries and daily hits has been moved to searchpromotions_garbage_collect.
The old command has now been fully removed and if called will throw an error.
Some search promotions URLs and templates have now moved from the main admin search module into the search promotions module.
| Item | Old | New |
|---|---|---|
| URL name | wagtailsearch_admin:queries_chooser | wagtailsearchpromotions:chooser |
| URL name | wagtailsearch_admin:queries_chooserresults | wagtailsearchpromotions:queries_chooserresults |
| Template | wagtail/search/templates/wagtailsearch/queries/chooser/chooser.html | wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/queries/chooser/chooser.html |
| Template | wagtail/search/templates/wagtailsearch/queries/chooser/results.html | wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/queries/chooser/results.html |
| Template | wagtail/search/templates/wagtailsearch/queries/chooser_field.html | wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/queries/chooser_field.html |
Block.get_template now accepts a value argumentThe get_template method on StreamField blocks now accepts a value argument in addition to context. Code using the old signature should be updated:
# Old
def get_template(self, context=None):
...
# New
def get_template(self, value=None, context=None):
...