Wagtailmenus 3.0 release notes¶
What’s new?¶
Wagtailmenus now ALWAYS uses ‘specific’ page data to rendering menus¶
Previously, developers needed to specify how specific pages should be fetched when rendering menus. The purpose of this was to keep performance optimal in projects where access to specific field values or method overrides was unnecessary for rendering menus. However, this flexibility came with a trade off: Making an educated decision about which option to use required some in-depth understanding of how some things in Wagtail work. The options were also slightly different for between menu types, and were difficult to explain in a way that made sense to everyone.
The ‘default’ behaviour was also sub-optimal, as additional queries would often
be made to fetch individual instances of MenuPage
or``AbstractLinkPage``.
If used quite widely, this could quietly push up the number of database queries
to a point where fetching the specific pages in advance would have been
far more performant.
In the case of ‘main’ and ‘flat’ menus, separate queries were also used to
fetch page data for top and sub levels, resorting in two calls to
get_base_page_queryset()
, as well as individual queries to fetch specific
page data for each top-level item.
These issues are all resolved by always fetching specific page data,
and doing so as efficiently as possible, using a single call to
PageQuerySet.specific()
. While this may may will hurt performance slightly
in some scenarios, from what I understand about how developers use
wagtailmenus, performance should be improved in a lot of cases, and
working with wagtailmenus should easier as a result of these changes.
See the upgrade considerations section below for notes on how these changes might affect your project.
Minor changes & bug fixes¶
Added support for Django 2.2 (no code changes necessary).
Added support for Wagtail 2.5 (no code changes necessary).
Added support for Wagtail 2.6 (no code changes necessary).
Added support for Wagtail 2.7.
Added support for Python 3.8.
Removed recommendation / automatic integration for
wagtail-condensedinlinepanel
.Optimised ‘derive page from URL’ and ‘derive section root’ logic.
Fixed bug #329, which prevented level-specific template naming from working as specified in the docs.
Fixed bug #323, which prevented
StreamField
from working properly when creating or editing menus with custom item models.
Upgrade considerations¶
Changes to Menu.get_sub_menu_templates()
¶
If you are overriding this method on a custom menu class you will need to update the method’s signature to accept a level
argument with a default value of 2
. If you are calling super().get_sub_menu_templates()
from within the override method, you should also pass the level
value through to that, like so:
MyCustomMenu(OriginalMenuClass):
def get_sub_menu_templates(self, level=2):
# your custom here
...
# passing level on to super() version
return super().get_sub_menu_templates(level)
Menu models no longer have a use_specific
field¶
If you are subclassing AbstractMainMenu
or AbstractFlatMenu
to create
custom menu models for your project, be sure to run Django’s makemigrations
command to remove the field from your custom models.
Also be sure to remove any reference to the field from custom panels definitions or model methods.
Menu tags no longer support the use_specific
option¶
If you are using this option with any of the built-in template tags in your page or menu templates, you should remove the use of the option, as it is now defunct.
For example, this:
{% main_menu max_levels=2 use_specific=3 %}
Should be simplified to:
{% main_menu max_levels=2 %}
Settings to control default ‘specific’ usage have been removed¶
If you are using either of the following settings to override default wagtailmenus behaviour in your project, you should clean up your settings file by removing the relevant lines, as they are now defunct.
WAGTAILMENUS_DEFAULT_CHILDREN_MENU_USE_SPECIFIC
WAGTAILMENUS_DEFAULT_SECTION_MENU_USE_SPECIFIC
Hooks no longer receive the use_specific
keyword argument¶
If your project uses these hooks to conditionally change something based
on this value, you should revise your code to assume that specific pages
are always being used. If the use_specific
argument is included in
your hook function signature, you should probably remove from there also.
Changes to Menu.get_pages_for_display()
¶
If you are using a custom main or flat menu class in your project that
overrides this method, you should ensure your custom method is updated
to fetch page data for top-level menu items as well as for sub levels (
use super()
where possible, and modify the result of that).
You should also avoid referencing top_level_items
or
get_top_level_items()
from within this method, as doing so will now
result in a circular reference (use get_base_menuitem_queryset()
to access menu item data instead).
Changes to Menu.get_base_menuitem_queryset()
¶
This method now uses select_related() to prefetch a few page fields for menu items that link to pages. If you’re overriding this method, or using the menus_modify_base_menuitem_queryset hook to alter the queryset, you might want to review your code to ensure you’re not adding additional complexity to the query unnecessarily.
If you are doing anything to limit the result based on page-specific
values, you should look at overriding get_base_page_queryset()
instead,
as any menu items linking to pages should only ever be displayed if the
page data is included in that resulting queryset.
Menu.pages_for_display
now returns a dictionary¶
If you have any custom menu functionality that depends on this value being a list or queryset, you’ll need to update your code to account for the fact that the return value is now a dictionary of page objects, keyed by page id.
In the case of main and flat menus, pages for the top-level menu items will also be included in the return value, in addition to those needed for sub menus.
Menu.clear_page_cache()
has been removed¶
Menu instances are intended to be rendered only once after being prepared for rendering. So, this method (originally added to aid with testing) no longer serves any useful purpose.
Menu.set_use_specific()
has been removed¶
This method is defunct, as specific page data is always used to render menus.
Menu.set_max_levels()
has been removed¶
A menu instance’s max_levels
attribute value is simply set directly in
Menu.prepare_to_render()
where required.
Planned removals¶
Following a standard deprecation period a two minor releases, the following functionality has now been removed.
Menu.get_instance_for_rendering()
¶
In an effort to make method names more reflective of their functionality, this method has been replaced by two methods:
create_from_collected_values()
and get_from_collected_values()
. The former is implemented on menu classes that are not model based (where instances must be created from scratch each time, for example: ChildrenMenu
, SectionMenu
, SubMenu
), and the latter is implemented on model-based menu classes, where a corresponding object must be retrieved from the database (so, AbstractMainMenu
, MainMenu
, AbstractFlatMenu
and FlatMenu
).
render_from_tag()
automatically calls one or the other, depending on whether the class inherits from django.db.models.Model
.
If you’re using custom menu classes in your project, and are overriding get_instance_for_rendering()
for any of those classes, you should update your code to override one of the new methods instead. Both of these new methods accept the same arguments, and return the same values, so the transition should be very easy.
Menu.get_contextual_vals_from_context()
¶
In an effort to make method names more reflective of their functionality, and to help dissuade users from overriding functionality that could be subject to change in future, this method has been renamed to _create_contextualvals_obj_from_context()
(becoming a private method in the process).
Menu.get_option_vals_from_options()
¶
In an effort to make method names more reflective of their functionality, and to help dissuade users from overriding functionality that could be subject to change in future, this method has been renamed to _create_optionvals_obj_from_values()
(becoming a private method in the process).