docs/releases/4.0.md
August 31, 2022
---
local:
depth: 1
---
This release adds support for Django 4.1. When upgrading, please note that the django-taggit library also needs to be updated to 3.0.0 or above.
The new BaseGenericSetting base model class allows defining a settings model that applies to all sites rather than just a single site.
See the Settings documentation for more information. This feature was implemented by Kyle Bayliss.
When using a queryset to render a list of images, you can now use the prefetch_renditions() queryset method to prefetch the renditions needed for rendering with a single extra query, similar to prefetch_related. If you have many renditions per image, you can also call it with filters as arguments - prefetch_renditions("fill-700x586", "min-600x400") - to fetch only the renditions you intend on using for a smaller query. For long lists of images, this can provide a significant boost to performance. See for more examples. This feature was developed by Tidiane Dia and Karl Hobley.
Following from Wagtail 3.0, this release contains significant UI changes that affect all of Wagtail's admin, largely driven by the implementation of the new Page Editor. These include:
Further updates to the page editor are expected in the next release. Those changes were implemented by Thibaud Colas. Development on this feature was sponsored by Google.
(rich_text_improvements_4)=
As part of the page editor redesign project sponsored by Google, we have made several improvements to our rich text editor:
1. to a list item. It’s now possible to un-do this change and keep the text as-is. This works for all Markdown-style shortcuts.Wagtail’s page preview is now available in a side panel within the page editor. This preview auto-updates as users type, and can display the page in three different viewports: mobile, tablet, desktop. The existing preview functionality is still present, moved inside the preview panel rather than at the bottom of the page editor. The auto-update delay can be configured with the WAGTAIL_AUTO_UPDATE_PREVIEW_INTERVAL setting. This feature was developed by Sage Abdullah.
In Wagtail 2.12, we introduced theming support for Wagtail’s primary brand color. This has now been extended to almost all of Wagtail’s color palette. View our documentation for more information, an overview of Wagtail’s customizable color palette, and a live demo of the supported customizations. This was implemented by Thibaud Colas, under the page editor redesign project sponsored by Google.
In Wagtail 2.16, we introduced support for Windows High Contrast mode (WHCM). This release sees a lot of improvements to our support, thanks to our new contributor Anuja Verma, who has been working on this as part of the Contrast Themes Google Summer of Code project, with support from Jane Hughes, Scott Cranfill, and Thibaud Colas.
In Wagtail 3.0, a new Page Editor experience was introduced, this release brings many of the UX and UI improvements to other parts of Wagtail for a more consistent experience. The bulk of these enhancements have been from Paarth Agarwal, who has been doing the UX Unification internship project alongside other Google Summer of Code participants. This internship has been sponsored by Torchbox with mentoring support from LB (Ben Johnston), Thibaud Colas and Helen Chapman.
autofocusexplorer_breadcrumb template tag to breadcrumbs as it is now used in multiple locationsModelAdmin usage of breadcrumbs completely and adopt consistent 'back' link approachSnippets can now be given a previewable HTML representation, revision history, and draft / live states through the use of the mixins PreviewableMixin, RevisionMixin, and DraftStateMixin. For more details, see:
These features were developed by Sage Abdullah.
The documentation now has dark mode which will be turned on by default if set in your browser or OS preferences, it can also be toggled on and off manually. The colors and fonts of the documentation now align with the design updates introduced in Wagtail 3.0. These features were developed by Vince Salvino.
There are also many improvements to the documentation both under the hood and in the layout;
base_url_path to ModelAdmin so that the default URL structure of app_label/model_name can be overridden (Vu Pham, Khanh Hoang)full_url to the API output of ImageRenditionField (Paarth Agarwal)InlinePanel's label when available for field comparison label (Sandil Ranasinghe)FormData instead of jQuery's form.serialize when editing documents or images just added so that additional fields can be better supported (Stefan Hammer)wagtail.admin.views.generic (Matt Westcott)wagtail.admin.widgets.chooser.BaseChooser to make it easier to build custom chooser inputs (Matt Westcott)WAGTAIL_ENABLE_UPDATE_CHECK = 'lts' (Tibor Leupold)prefetch_renditions method to ImageQueryset for performance optimization on image listings (Tidiane Dia, Karl Hobley)get_field_clean_name method when defining FormField models that extend AbstractFormField (LB (Ben) Johnston)core.css file (Thibaud Colas)ReportView to extend from generic wagtail.admin.views.generic.models.IndexView (Sage Abdullah)Unpublish view to extend from generic wagtail.admin.views.generic.models.UnpublishView (Sage Abdullah)wagtail.admin.viewsets.chooser.ChooserViewSet module to serve as a common base implementation for chooser modals (Matt Westcott)wagtail.admin.viewsets.model.ModelViewSet (Matt Westcott)add_to_admin_menu option for ModelAdmin (Oliver Parker)PermissionHelper (Tidiane Dia)boost works when using Postgres with the database search backend (Tibor Leupold)django-filter version to support 22 (Yuekui).iterator() in a few more places in the admin, to make it more stable on sites with many pages (Andy Babic)wagtail.contrib.modeladmin.menus.SubMenu class, provide a warning if used directing developers to use wagtail.admin.menu.Menu instead (Matt Westcott)WAGTAILADMIN_USER_PASSWORD_RESET_FORM setting for overriding the admin password reset form (Michael Karamuth)classnames template tag to easily build up classes from variables provided to a template (Paarth Agarwal)ModelAdmin InspectView footer actions consistent with other parts of the UI (Thibaud Colas)menu_item_name to modify MenuItem's name for ModelAdmin (Alexander Rogovskyy, Vu Pham)blocks_by_name and first_block_by_name methods on StreamValue (Tidiane Dia, Matt Westcott)is_parent kwarg in various page button hooks as this approach is no longer required (Paarth Agarwal)BadSignature error (Jaap Roes)path and re_path decorators to the RoutablePageMixin module which emulate their Django URL utils equivalent, redirect re_path to the original route decorator (Tidiane Dia)BaseChooser widget now provides a Telepath adapter that's directly usable for any subclasses that use the chooser widget and modal JS as-is with no customizations (Matt Westcott)ModelAdmin index listings with export list enabled would show buttons with an incorrect layout (Josh Woodcock)ResumeWorkflowActionFormatter message (Stefan Hammer)PageRevision with generic Revision model (Sage Abdullah)aria-label is not set on locale selection dropdown within page chooser modal as it was a duplicate of the button contents (LB (Ben Johnston))ModelAdmin title column behavior to only link to 'edit' if the user has the correct permissions, fallback to the 'inspect' view or a non-clickable title if needed (Stefan Hammer)DecimalBlock preserves the Decimal type when retrieving from the database (Yves Serrano)ngettext in Wagtail's internal JavaScript internationalisation utilities now works (LB (Ben) Johnston)ModelAdmin single selection lists show correctly with Django 4.0 form template changes (Coen van der Kamp)AttributeError when an empty search param q= is combined with other filters in the Images index view (Paritosh Kabra)BaseSiteSetting / BaseGenericSetting objects can be pickled (Andy Babic)DocumentChooserBlock can be deconstructed for migrations (Matt Westcott)BaseSetting when upgrading to Wagtail 4.0 (Stefan Hammer)updatemodulepaths command for Python 3.7 (Matt Westcott)Page.serve() and Page.serve_preview() methodsAs part of making previews available to non-page models, the serve_preview() method has been decoupled from the serve() method and extracted into the PreviewableMixin class. If you have overridden the serve() method in your page models, you will likely need to override serve_preview(), get_preview_template(), and/or get_preview_context() methods to handle previews accordingly. Alternatively, you can also override the preview_modes property to return an empty list to disable previews.
The live preview panel utilizes an iframe to display the preview in the editor page, which requires the page in the iframe to have the X-Frame-Options header set to SAMEORIGIN (or unset). If you click a link within the preview panel, you may notice that the iframe stops working. This is because the link is loaded within the iframe and the linked page may have the X-Frame-Options header set to DENY. To work around this problem, add the following <base> tag within your <head> element in your base.html template, before any <link> elements:
{% if request.in_preview_panel %}
<base target="_blank">
{% endif %}
This will make all links in the live preview panel open in a new tab.
As of Wagtail 4.0.1, new Wagtail projects created through the wagtail start command already include this change in the base template.
base_url_path keyword argument added to AdminURLHelperThe wagtail.contrib.modeladmin.helpers.AdminURLHelper class now accepts a base_url_path keyword argument on its constructor. Custom subclasses of this class should be updated to accept this keyword argument.
Safari 13 will no longer be officially supported as of this release, this deviates the current support for the last 3 version of Safari by a few months and was required to add better support for RTL languages.
PageRevision replaced with RevisionThe PageRevision model has been replaced with a generic Revision model. If you use the PageRevision model in your code, make sure that:
PageRevision objects should be updated to create Revision objects using the page's id as the object_id, the default Page model's content type as the base_content_type, and the page's specific content type as the content_type.PageRevision.objects manager should be updated to use the Revision.page_revisions manager.Revision queries that use Page.id should be updated to cast the Page.id to a string before using it in the query (e.g. by using str() or Cast("page_id", output_field=CharField())).Page queries that use PageRevision.page_id should be updated to cast the Revision.object_id to an integer before using it in the query (e.g. by using int() or Cast("object_id", output_field=IntegerField())).PageRevision.page should be updated to Revision.content_object.If you maintain a package across multiple Wagtail versions that includes a model with a ForeignKey to the PageRevision model, you can create a helper function to correctly resolve the model depending on the installed Wagtail version, for example:
from django.db import models
from wagtail import VERSION as WAGTAIL_VERSION
def get_revision_model():
if WAGTAIL_VERSION >= (4, 0):
return "wagtailcore.Revision"
return "wagtailcore.PageRevision"
class MyModel(models.Model):
# Before
# revision = models.ForeignKey("wagtailcore.PageRevision")
revision = models.ForeignKey(get_revision_model(), on_delete=models.CASCADE)
Page.get_latest_revision_as_page renamed to Page.get_latest_revision_as_objectThe Page.get_latest_revision_as_page method has been renamed to Page.get_latest_revision_as_object. The old name still exists for backwards-compatibility, but calling it will raise a RemovedInWagtail50Warning.
AdminChooser replaced with BaseChooserCustom choosers should no longer use wagtail.admin.widgets.chooser.AdminChooser which has been replaced with wagtail.admin.widgets.chooser.BaseChooser.
get_snippet_edit_handler moved to wagtail.admin.panels.get_edit_handlerThe get_snippet_edit_handler function in wagtail.snippets.views.snippets has been moved to get_edit_handler in wagtail.admin.panels.
explorer_breadcrumb template tag has been renamed to breadcrumbs, move_breadcrumb has been removedThe explorer_breadcrumb template tag is not documented, however if used it will need to be renamed to breadcrumbs and the url_name is now a required arg.
The move_breadcrumb template tag is no longer used and has been removed.
wagtail.contrib.modeladmin.menus.SubMenu is deprecatedThe wagtail.contrib.modeladmin.menus.SubMenu class should no longer be used for constructing submenus of the admin sidebar menu. Instead, import wagtail.admin.menu.Menu and pass the list of menu items as the items keyword argument.
The internal JavaScript functions createPageChooser, createSnippetChooser, createDocumentChooser and createImageChooser used for initializing chooser widgets have been replaced by classes, and user code that calls them needs to be updated accordingly:
createPageChooser(id) should be replaced with new PageChooser(id)createSnippetChooser(id) should be replaced with new SnippetChooser(id)createDocumentChooser(id) should be replaced with new DocumentChooser(id)createImageChooser(id) should be replaced with new ImageChooser(id)If your code contains references to URL route names within the wagtailimages, wagtaildocs or wagtailsnippets namespaces, these should be updated as follows:
wagtailimages:chooser is now wagtailimages_chooser:choosewagtailimages:chooser_results is now wagtailimages_chooser:choose_resultswagtailimages:image_chosen is now wagtailimages_chooser:chosenwagtailimages:chooser_upload is now wagtailimages_chooser:createwagtailimages:chooser_select_format is now wagtailimages_chooser:select_formatwagtaildocs:chooser is now wagtaildocs_chooser:choosewagtaildocs:chooser_results is now wagtaildocs_chooser:choose_resultswagtaildocs:document_chosen is now wagtaildocs_chooser:chosenwagtaildocs:chooser_upload is now wagtaildocs_chooser:createwagtailsnippets:list, wagtailsnippets:list_results, wagtailsnippets:add, wagtailsnippets:edit, wagtailsnippets:delete-multiple, wagtailsnippets:delete, wagtailsnippets:usage, wagtailsnippets:history: These now exist in a separate wagtailsnippets_{app_label}_{model_name} namespace for each snippet model, and no longer take app_label and model_name as arguments.wagtailsnippets:choose, wagtailsnippets:choose_results, wagtailsnippets:chosen: These now exist in a separate wagtailsnippetchoosers_{app_label}_{model_name} namespace for each snippet model, and no longer take app_label and model_name as arguments.As part of the introduction of the new live preview panel, we have changed the WAGTAIL_AUTO_UPDATE_PREVIEW setting to be on (True) by default. This can still be turned off by setting it to False. The WAGTAIL_AUTO_UPDATE_PREVIEW_INTERVAL setting has been introduced for sites willing to reduce the performance cost of the live preview without turning it off completely.
The page explorer listings now use Wagtail’s new slim header, replacing the previous large teal header. The parent page’s metadata and related actions are now available within the “Info” side panel, while the majority of buttons are now available under the Actions dropdown in the header, identically to the page create/edit forms.
Customizing which actions are available and adding extra actions is still possible, but has to be done with the register_page_header_buttons hook, rather than register_page_listing_buttons and register_page_listing_more_buttons. Those hooks still work as-is to define actions for each page within the listings.
is_parent removed from page button hooksconstruct_page_listing_buttons, register_page_listing_buttons, register_page_listing_more_buttons no longer accept the is_parent keyword argument and this should be removed.is_parent was the previous approach for determining whether the buttons would show in the listing rows or the page's more button, this can be now achieved with discrete hooks instead.As part of our support for theming across all colors, we’ve had to rename or remove some of the pre-existing CSS variables. Wagtail’s indigo is now customizable with --w-color-primary, and the teal is customizable as --w-color-secondary. See for an overview of all customizable colors. Here are replaced variables:
--color-primary is now --w-color-secondary--color-primary-hue is now --w-color-secondary-hue--color-primary-saturation is now --w-color-secondary-saturation--color-primary-lightness is now --w-color-secondary-lightness--color-primary-darker is now --w-color-secondary-400--color-primary-darker-hue is now --w-color-secondary-400-hue--color-primary-darker-saturation is now --w-color-secondary-400-saturation--color-primary-darker-lightness is now --w-color-secondary-400-lightness--color-primary-dark is now --w-color-secondary-600--color-primary-dark-hue is now --w-color-secondary-600-hue--color-primary-dark-saturation is now --w-color-secondary-600-saturation--color-primary-dark-lightness is now --w-color-secondary-600-lightness--color-primary-lighter is now --w-color-secondary-100--color-primary-lighter-hue is now --w-color-secondary-100-hue--color-primary-lighter-saturation is now --w-color-secondary-100-saturation--color-primary-lighter-lightness is now --w-color-secondary-100-lightness--color-primary-light is now --w-color-secondary-50--color-primary-light-hue is now --w-color-secondary-50-hue--color-primary-light-saturation is now --w-color-secondary-50-saturation--color-primary-light-lightness is now --w-color-secondary-50-lightnessWe’ve additionally removed all --color-input-focus and --color-input-focus-border variables, as Wagtail’s form fields no longer have a different color on focus.
WAGTAILDOCS_DOCUMENT_FORM_BASE and WAGTAILIMAGES_IMAGE_FORM_BASE must inherit from BaseDocumentForm / BaseImageFormPreviously, it was valid to specify an arbitrary model form as the WAGTAILDOCS_DOCUMENT_FORM_BASE / WAGTAILIMAGES_IMAGE_FORM_BASE settings. This is no longer supported; these forms must now inherit from wagtail.documents.forms.BaseDocumentForm and wagtail.images.forms.BaseImageForm respectively.
As part of the page editor redesign, we have removed support for the classname="full" customization to panels. Existing title and collapsed customizations remain unchanged.
route decorator for RoutablePageMixinroute decorator at this time.RoutablePageMixin contrib module now provides a path decorator that behaves the same way as Django's {func}django.urls.path function.RoutablePageMixin's route decorator will now redirect to a new re_path decorator that emulates the behavior of {func}django.urls.re_path.BaseSetting model replaced by BaseSiteSettingThe wagtail.contrib.settings.models.BaseSetting model has been replaced by two new base models BaseSiteSetting and BaseGenericSetting, to accommodate settings that are shared across all sites. Existing setting models that inherit BaseSetting should be updated to use BaseSiteSetting instead:
from wagtail.contrib.settings.models import BaseSetting, register_setting
@register_setting
class SiteSpecificSocialMediaSettings(BaseSetting):
facebook = models.URLField()
should become
from wagtail.contrib.settings.models import BaseSiteSetting, register_setting
@register_setting
class SiteSpecificSocialMediaSettings(BaseSiteSetting):
facebook = models.URLField()