Wagtail 7.0 (LTS) release notes

May 6, 2025

Wagtail 7.0 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).

What’s new

Django 5.2 support

This version adds formal support for Django 5.2.

Deferring validation on saving drafts

This release introduces a change to the validation behavior when saving pages (or snippets using DraftStateMixin) as drafts. In most cases, required fields will not be enforced when saving as draft, allowing users to save work-in-progress versions without filling in all fields. Validation is applied as normal when the page or snippet is published, scheduled, or submitted to a workflow. The new behavior is enabled by default, but see the notes below on Configuring deferred validation of required fields.

This feature was developed by Matt Westcott and Sage Abdullah.

New and improved pagination

We have a new pagination interface for all listing views and most choosers, including page numbers. This simplifies navigation for listings with tens or hundreds of pages, so users can jump directly to the last pages. Thank you to Jordan Teichmann for implementing the new designs, with guidance from Sage Abdullah.

Locale in listings and choosers

This release adds a new “Locale” column to the listings and choosers of translatable models, making it easier to filter and sort by locale. The current content’s locale is applied in choosers by default, with the ability to clear the locale filter. This feature was developed by Dan Braghis and Sage Abdullah.

How-to guide for Django Ninja

While Wagtail provides a built-in API module based on the popular Django REST Framework, it is possible to use other approaches. This release adds a new How to set up Django Ninja guide, to demonstrate basic usage of Wagtail with Django Ninja, which leverages type hints and Pydantic for data validation. The guide covers common requirements beyond initial setup, like rich text, image renditions, and generation of API documentation.

This documentation was contributed by Thibaud Colas with support from Sage Abdullah.

Other features

  • Add WAGTAIL_ prefix to Wagtail-specific tag settings (Aayushman Singh)

  • Implement normalize on TypedTableBlock to assist with setting default and preview_value (Sage Abdullah)

  • Apply normalization when modifying a StreamBlock’s value to assist with programmatic changes to StreamField (Matt Westcott)

  • Allow a custom image rendition model to define its unique constraint with models.UniqueConstraint instead of unique_together (Oliver Parker, Cynthia Kiser, Sage Abdullah)

  • Default to the standard tokenizer on Elasticsearch, to correctly handle numbers as tokens (Matt Westcott)

  • Add color-scheme meta tag to Wagtail admin (Ashish Nagmoti)

  • Add the ability to set the default privacy restriction for new pages using get_default_privacy_setting (Shlomo Markowitz)

  • Improve performance of batch purging page urls in the frontend cache, avoiding n+1 query issues (Andy Babic)

  • Add better support and documentation for overriding or extending icons used in the in the userbar (Sébastien Corbin)

  • List the comments action, if comments are enabled, within the admin keyboard shortcuts dialog (Dhruvi Patel)

  • Add better support and documentation for overriding the default field widgets used within form pages (Baptiste Mispelon)

  • Allow workflow tasks to specify a template for the action modal via get_template_for_action (Sage Abdullah)

  • Change ‘Publish’ button label to ‘Schedule to publish’ if go-live schedule is set (Sage Abdullah)

  • Hide add locale button when no more languages are available (Dan Braghis)

  • Allow customizing InspectView field display value via methods on the view (Dan Braghis)

  • Make rendering of active listing filters extensible, to support additional filter types (Sage Abdullah)

Bug fixes

  • Take preferred language into account for translatable strings in client-side code (Bernhard Bliem, Sage Abdullah)

  • Support translating with the preferred language for rich text formatting labels (Bernhard Bliem, Sage Abdullah)

  • Make “Actions” label translatable within the rich text toolbar (Bernhard Bliem, Sage Abdullah)

  • Do not show the content type column as sortable when searching pages (Srishti Jaiswal, Sage Abdullah)

  • Support simple subqueries for in and exact lookup on Elasticsearch (Sage Abdullah)

  • Force preview panel scroll behavior to instant to avoid flickering (Sage Abdullah)

  • Fix incorrect “Views (past week)” heading on promoted search results listing (Baptiste Mispelon)

  • Ensure InlinePanel will be correctly ordered after the first save when min_num is used (Elhussein Almasri, Joel William)

  • Avoid deprecation warnings about URLField assume_scheme on Django 5.x (Sage Abdullah)

  • Fix setup.cfg syntax for setuptools v78 (Sage Abdullah)

  • Ensure ImproperlyConfigured is thrown from db_field on unbound FieldPanels as intended (Matt Westcott)

  • Refine the positioning of the add comment button next to select, radio, checkbox fields and between field row columns (Srishti Jaiswal)

  • Show the correct privacy status for child collections of private collections (Shlomo Markowitz)

  • Ensure reference index correctly handles models with primary keys not named id (Sage Abdullah)

  • On “move page” bulk action, do not prefill the destination with the root page (Stefan Hammer)

  • Ensure the default preferred language respects the WAGTAILADMIN_PERMITTED_LANGUAGES setting (Sébastien Corbin)

  • Ensure there is consistent padding in homepage panels table headers (Aditya (megatrron))

  • Prevent redundant calls to context processors when rendering userbar (Ihor Marhitych)

  • Fix validation of duplicate field names in form builder when fields have been deleted (Ian Meigh)

  • Fix font size of footer action buttons when there is only one item (Sage Abdullah)

Documentation

  • Add missing django.contrib.admin to list of apps in “add to Django project” guide (Mohamed Rabiaa)

  • Add tutorial on deploying on Ubuntu to third-party tutorials (Mohammad Fathi Rahman)

  • Document that request_or_site is optional on BaseGenericSetting.load (Matt Westcott)

  • Mention third-party StreamField based form builder packages in the form builder documentation (Matt Westcott)

  • Clarify that insert_editor_js hook applies to all core editing/creation views (LB (Ben) Johnston)

  • Clarify requirement for non-page models using model mixins to be registered as snippets (Sage Abdullah)

  • Document the page.move method from django-treebeard (Shlomo Markowitz)

  • Clarify that Page.get_url_parts will return a tuple, not None for NoReverseMatch errors (Arthur Tripp)

  • Document the expand_db_html utility function to create HTML ready for display (Thibaud Colas)

  • Mention expand_db_html usage for rich text in REST framework API (Thibaud Colas)

  • Docs: Further document usage of API ?limit with WAGTAILAPI_LIMIT_MAX (Thibaud Colas)

Maintenance

  • Migrate away from deprecated Sass import rules to module system (Srishti Jaiswal)

  • Apply Sass mixed declarations migration in preparation for CSS nesting (Prabhpreet Kaur)

  • Refactor styles for Draftail, minimap, and comments to fix remaining Sass migration warnings (Thibaud Colas)

  • npm package updates; downshift, focus-trap-react, immer, redux, uuid (LB (Ben) Johnston)

  • Validate against invalid characters in Lexeme values (Matt Westcott)

  • Split up wagtail.models module into submodules (Matt Westcott)

  • Update ruff to 0.9.6 (Sage Abdullah)

  • Fix up stubs & adapter contents to better support Jest testing (LB (Ben) Johnston)

  • Cleanup Stimulus controller imports, JSDoc & linting (LB (Ben) Johnston)

  • Rename SkipLinkController to FocusController with improved reusability, updated unit tests, and added story (LB (Ben) Johnston)

  • Fix CI testing issues with the Stimulus LocaleController time zones & non-deterministic page ordering tests (Sage Abdullah)

  • Make GitHub highlight .html files as Django templates (Jake Howard)

  • Remove admin JavaScript imports from shared template includes, moving the imports to the appropriate core admin inclusion locations (Sai Srikar Dumpeti)

  • Remove non-editing view inclusions of the insert_editor_js hook output, deprecate the wrapper template tag _editor_js.html (Sai Srikar Dumpeti, LB (Ben) Johnston)

  • Remove upper bound on Django dependency (Matt Westcott)

  • Add default_auto_field setting to home app in project template (Sylvain Boissel)

  • Migrate setuptools configuration from setup.py and setup.cfg to pyproject.toml (Sage Abdullah)

  • Refactor move_choose_destination to a class-based view (Chiemezuo Akujobi)

  • Update django-tasks to 0.7.x (Jake Howard)

Upgrade considerations - removal of deprecated features from Wagtail 5.2 - 6.3

Features previously deprecated in Wagtail 5.2, 6.0, 6.1, 6.2 and 6.3 have been fully removed. For additional details on these changes, see:

The most significant changes are highlighted below.

Removal of classnames attribute on menu item and image format classes

The classnames keyword argument on the following classes is no longer supported and should be replaced with classname:

  • admin.menu.MenuItem

  • admin.ui.sidebar.ActionMenuItem

  • admin.ui.sidebar.LinkMenuItem

  • admin.ui.sidebar.PageExplorerMenuItem

  • contrib.settings.registry.SettingMenuItem

  • wagtail.images.formats.Format

Renamed settings

Replaced mechanism for customizing user form classes

The settings WAGTAIL_USER_EDIT_FORM, WAGTAIL_USER_CREATION_FORM and WAGTAIL_USER_CUSTOM_FIELDS have been removed in favor of customizing the form classes via UserViewSet.get_form_class().

Updates to button rendering hooks

Other removals

  • The get_template method on StreamField blocks now accepts a value argument as its first argument.

  • Passing a dict as DISTRIBUTION_ID within the WAGTAILFRONTENDCACHE configuration setting is no longer supported, and should be replaced by multiple backends with a HOSTNAMES parameter.

  • ModelViewSet no longer provides the URL patterns <int:pk>/ and <int:pk>/delete/ for editing and deleting; these have been replaced by edit/<str:pk>/ and delete/<str:pk>/.

  • The undocumented usage of the JavaScript window.chooserUrls within Draftail choosers is removed.

  • The undocumented coreutils.escape_script function and escapescript template tag, and handling of <script type="text/django-form-template"> elements, are removed.

  • The undocumented template tags js_translation_strings, locales and user_listing_buttons are removed.

  • The following undocumented global Javascript functions are removed: window.initBlockWidget, window.enableDirtyFormCheck, window.URLify, window.ActivateWorkflowActionsForDashboard, window.ActivateWorkflowActionsForEditView.

  • The undocumented JavaScript variable window.wagtailConfig.BULK_ACTION_ITEM_TYPE is removed.

  • Support for the data-tippy-content attribute is removed.

  • The undocumented DeleteMenuItem API is removed.

Upgrade considerations - changes affecting all projects

Configuring deferred validation of required fields

For text-based fields (such as CharField, TextField, RichTextField and StreamField), the new behaviour of skipping required field validation on saving drafts is in place automatically, with no code changes required. For non-text-based fields (such as IntegerField and DateField) that are not defined with null=True, the database will not allow saving of blank values, and so the form will continue to enforce these as required fields even when saving as draft.

To allow a non-text-based field to be left blank on saving drafts, add null=True to its definition. The blank argument specifies whether the field is required on publish, and should be omitted (or set to blank=False) unless you intend the field to be fully optional. For example, the definition:

    date_published = models.DateField("Date article published")

can be changed as follows to allow it to be left blank when saving as draft:

    date_published = models.DateField("Date article published", null=True)

To strictly enforce requiredness on a field, including when saving as draft, you can set the attribute required_on_save = True on the model field, or pass required_on_save=True as an argument to FieldPanel. For example:

    subtitle = models.CharField(max_length=255)
    subtitle.required_on_save = True

or

    content_panels = Page.content_panels + [
        FieldPanel("subtitle", required_on_save=True)
    ]

This option is enabled as standard for the title field of page models. It is also recommended to use this option for any fields that are used in the __str__ representation of snippet models, so that these models always have a meaningful representation within listing views.

Page.save() no longer automatically calls full_clean for draft pages

In previous releases, the save() method on page models called the full_clean method to apply model-level validation rules, regardless of whether the page was in a draft or live state, unless this was explicitly disabled by passing clean=False. As of this release, saving a page in a draft state (live=False) will only perform the minimum validation necessary to ensure data integrity: the title must be non-empty, and the slug must be unique within the parent page. Saving a page with live=True will apply full validation as before. If you have user code that creates draft pages and requires them to be validated, you must now call full_clean explicitly.

“Snippets” menu now only includes snippet models without menu items

The “Snippets” sidebar menu item appears if there are snippet models without their own menu items. Previously, the “Snippets” menu item pointed to a snippets index view that listed all snippet models whether they’d been configured with their own menu items or not. This behaviour has been changed and the snippets index view will now only include snippet models that haven’t been configured that way.

The new WAGTAILSNIPPETS_MENU_SHOW_ALL setting can be used to always show a top-level “Snippets” menu item in the sidebar pointing to an index view that includes all snippet models.

Upgrade considerations - deprecation of old functionality

TAG_LIMIT and TAG_SPACES_ALLOWED settings renamed to WAGTAIL_TAG_LIMIT and WAGTAIL_TAG_SPACES_ALLOWED

The TAG_LIMIT and TAG_SPACES_ALLOWED settings have been renamed to WAGTAIL_TAG_LIMIT and WAGTAIL_TAG_SPACES_ALLOWED respectively. The old settings will continue to work for now, but will be removed in a future release.

Custom listing views must now have breadcrumbs

If your project has custom listing views in the admin that make use of the wagtailadmin/generic/index.html template but do not provide a breadcrumbs_items context variable, you will need to add this context variable to your view.

The breadcrumbs_items context variable is used to display the breadcrumbs in the admin interface as part of the Universal Listings project. This should be done automatically by the get_breadcrumbs_items method in the wagtail.admin.views.generic.base.BaseListingView class (or its subclasses, e.g. wagtail.admin.views.generic.IndexView).

If you have a custom listing view that does not inherit from the BaseListingView class, you will need to add the breadcrumbs_items context variable manually. Once added, the title header will be replaced by the breadcrumbs and the filters on the right sidebar will be replaced by the new AJAX-based filters pop-up in the header.

For now, listing views with no breadcrumbs will continue to have the title header and the legacy filters on the right sidebar, but the support will be removed in a future release.

Upgrade considerations - changes to undocumented internals

Removal of insert_editor_js hook output in some non-editor views

The insert_editor_js was historically added to some non editing views, these have now been removed.

The confirm bulk move view and the chooser modal view for choosing the new location of bulk move pages will no longer use the hook output insert_editor_js. If custom JavaScript is needed in these views, migrate to the documented insert_global_admin_js hook instead.

The insert_editor_js hook will continue to be output when editing pages, as documented.

Deprecation of wagtailadmin/pages/_editor_js.html template

The undocumented template partial wagtailadmin/pages/_editor_js.html has been deprecated and will be removed in a future release.

If your project overrides the template to inject custom JavaScript into the Wagtail admin, you should follow the documented approaches to either use the insert_editor_js hook or the insert_global_admin_js hook instead.

Removal of wagtailadmin/shared/ajax_pagination_nav.html template

The undocumented template partial wagtailadmin/shared/ajax_pagination_nav.html was marked for soft deprecation in Wagtail 2.16 and has now been removed.

If you use or override the template, you should use wagtailadmin/shared/pagination_nav.html with meaningful URLs as the linkurl value instead.