# Wagtail 7.0 (LTS) release notes

_May 6, 2025_

```{contents}
---
local:
depth: 1
---
```

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`](wagtailsnippets_saving_draft_changes_of_snippets)) 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).

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](api) based on the popular Django REST Framework, it is possible to use other approaches.
This release adds a new [](api_ninja) guide, to demonstrate basic usage of Wagtail with [Django Ninja](https://django-ninja.dev/), 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](set_default_page_privacy) 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](custom_icons_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](custom_form_field_type_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 `FieldPanel`s 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](../advanced_topics/add_to_django_project) (Mohamed Rabiaa)
 * Add tutorial on deploying on Ubuntu to [third-party tutorials](../advanced_topics/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](form_builder) documentation (Matt Westcott)
 * Clarify that [`insert_editor_js` hook](insert_editor_js) 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](rich_text_manual_conversion) to create HTML ready for display (Thibaud Colas)
 * Mention `expand_db_html` usage for rich text in [REST framework API](api_v2_configuration) (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:

 * [Wagtail 5.2 release notes](/releases/5.2)
 * [Wagtail 6.0 release notes](/releases/6.0)
 * [Wagtail 6.1 release notes](/releases/6.1)
 * [Wagtail 6.2 release notes](/releases/6.2)
 * [Wagtail 6.3 release notes](/releases/6.3)

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

 * The `WAGTAIL_AUTO_UPDATE_PREVIEW` setting is removed and should be replaced with [`WAGTAIL_AUTO_UPDATE_PREVIEW_INTERVAL = 0`](wagtail_auto_update_preview_interval) to disable auto-update.
 * The `PASSWORD_REQUIRED_TEMPLATE` setting is no longer recognized and should be replaced with [`WAGTAIL_PASSWORD_REQUIRED_TEMPLATE`](frontend_authentication).
 * The `DOCUMENT_PASSWORD_REQUIRED_TEMPLATE` setting is no longer recognized and should be replaced with [`WAGTAILDOCS_PASSWORD_REQUIRED_TEMPLATE`](frontend_authentication).

### 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()`](custom_userviewset).

### Updates to button rendering hooks

* The hooks [`register_page_header_buttons`](register_page_header_buttons), [`register_page_listing_buttons`](register_page_listing_buttons), [`construct_page_listing_buttons`](construct_page_listing_buttons) and [`register_page_listing_more_buttons`](register_page_listing_more_buttons) now receive a `user` argument instead of `page_perms`.
* The [`register_page_header_buttons`](register_page_header_buttons) receives a fourth argument `view_name`.
* The [`construct_snippet_listing_buttons`](construct_snippet_listing_buttons) hook no longer accepts a `context` argument.
* The [`register_user_listing_buttons`](register_user_listing_buttons) hook now accepts a `request_user` argument instead of `context`.

### 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](frontendcache_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)=

### 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:

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

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

```python
    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`](FieldPanel.required_on_save) as an argument to `FieldPanel`. For example:

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

or

```python
    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](inv:django#validating-objects), 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](wagtailsnippets_menu_item).
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.
