Welcome to the wagtailmenus documentation!¶
wagtailmenus is an open-source extension for Wagtail CMS to help you define, manage and render menus in a consistent, yet flexible way.
The current version is tested for compatiblily with the following:
- Wagtail versions 1.10 to 2.0
- Django versions 1.10 to 2.0
- Python versions 3.4 to 3.6
To find out more about what wagtailmenus does and why, see Overview and key concepts
To view the code, open an issue, or submit a pull request, view the wagtailmenus project on github.
Below are some useful links to help you get you started:
- Rendering menus
- Optional page models
Full index¶
Overview and key concepts¶
- Better control over top-level menu items
- Link to pages, custom URLs, or a combination of both
- Multi-level menus generated from your existing page tree
- Define menus for all your project needs
- Suitable for single-site or multi-site projects
- Solves the problem of important page links becoming just ‘toggles’ in multi-level menus
- Use the default menu templates for rendering, or easily add your own
Better control over top-level menu items¶
When you have a ‘main navigation’ menu powered purely by the page tree, things can get a little tricky, as they are often designed in a way that is very sensitive to change. Some moderators might not understand that publishing a new page at the top level (or reordering those pages) will dramatically affect the main navigation (and possibly even break the design). And really, why should they?
Wagtailmenus solves this problem by allowing you to choose exactly which pages should appear as top-level items. It adds new functionality to Wagtail’s CMS, allowing you to simply and effectively create and manage menus, using a familiar interface.
You can also use Wagtail’s built-in group permissions system to control which users have permission to make changes to menus.
Link to pages, custom URLs, or a combination of both¶
The custom URL field won’t force you to enter a valid URL, so you can add things like #request-callback
or #signup
to link to areas on the active page (perhaps as JS modals).
You can also define additional values to be added to a page’s URL, letting you jump to a specific anchor point on a page, or include fixed GET parameters for analytics or to trigger custom functionality.
Multi-level menus generated from your existing page tree¶
We firmly beleive that your page tree is the best place to define the structure, and the ‘natural order’ of pages within your site. Wagtailmenus only allows you to define the top-level items for each menu, because offering anything more would inevitably lead to site managers redefining parts of the page tree in multiple places, doomed to become outdated as the original tree changes over time.
To generate multi-level menus, wagtailmenus takes the top-level items you define for each menu and automatically combines it with your page tree, efficiently identifying ancestors for each selected pages and outputting them as sub-menus following the same structure and order.
You can prevent any page from appearing menus simply by setting show_in_menus
to False
. Pages will also no longer be included in menus if they are unpublished.
Define menus for all your project needs¶
Have you ever hard-coded a menu into a footer at the start of a project, only for those pages never to come into existence? Or maybe the pages were created, but their URLs changed later on, breaking the hard-coded links? How about ‘secondary navigation’ menus in headers?
As well as giving you control over your ‘main menu’, wagtailmenus allows you to manage any number of additional menus via the CMS as ‘flat menus’, meaning they too can benefit from page links that dynamically update to reflect tree position or status changes.
Don’t hard-code another menu again! CMS-managed menus allow you to make those ‘emergency changes’ and ‘last-minute tweaks’ without having to touch a single line of code.
Note
Despite the name, ‘flat menus’ can be configured to render as multi-level menus if you need them to.
Suitable for single-site or multi-site projects¶
While main menus always have to be defined for each site, for flat menus, you can support multiple sites using any of the following approaches:
- Define a new menu for each site
- Define a menu for your default site and reuse it for the others
- Create new menus for some sites, but use the default site’s menu for others
You can even use different approaches for different flat menus in the same project. If you’d like to learn more, take a look at the fall_back_to_default_site_menus
option in Supported arguments
A copy feature in also available from the flat menu management interface, allowing you to quickly and easily copy existing menus from one site to another.
In a multi-site project, you can also configure wagtailmenus to use separate sets of templates for each site for rendering (See Using preferred paths and names for your templates)
Solves the problem of important page links becoming just ‘toggles’ in multi-level menus¶
Extend the wagtailmenus.models.MenuPage
model instead of the usual wagtail.wagtailcore.models.Page
model to create your custom page types, and gain a couple of extra fields that will allow you to configure certain pages to appear again alongside their children in multi-level menus. Use the menu tags provided, and that behaviour will remain consistent in all menus throughout your site. To find out more, see: The MenuPage and MenuPageMixin models
Use the default menu templates for rendering, or easily add your own¶
Each menu tag comes with a default template that’s designed to be fully accessible and compatible with Bootstrap 3. However, if you don’t want to use the default templates, wagtailmenus makes it easy to use your own, using whichever approach works best for you:
- Use settings to change the default templates used for each tag
- Specify templates using
template
andsub_menu_template
arguments for any of the included menu tags (See Specifying menu templates using template tag parameters). - Put your templates in a preferred location within your project and wagtailmenus will pick them up automatically (See Using preferred paths and names for your templates).
Installing wagtailmenus¶
Install the package using pip:
pip install wagtailmenus
Add
wagtailmenus
andwagtail.contrib.modeladmin
to theINSTALLED_APPS
setting in your project settings:INSTALLED_APPS = [ ... 'wagtail.contrib.modeladmin', # Don't repeat if it's there already 'wagtailmenus', ]
Add
wagtailmenus.context_processors.wagtailmenus
to thecontext_processors
list in yourTEMPLATES
setting. The setting should look something like this:TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', ' DIRS': [ os.path.join(PROJECT_ROOT, 'templates'), ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.contrib.auth.context_processors.auth', 'django.template.context_processors.debug', 'django.template.context_processors.i18n', 'django.template.context_processors.media', 'django.template.context_processors.request', 'django.template.context_processors.static', 'django.template.context_processors.tz', 'django.contrib.messages.context_processors.messages', 'wagtail.contrib.settings.context_processors.settings', 'wagtailmenus.context_processors.wagtailmenus', ], }, }, ]
Run migrations to create database tables for wagtailmenus:
python manage.py migrate wagtailmenus
This step is optional. If you’re adding wagtailmenus to an existing project, and the tree for each site follows a structure similar to the example below, you may find it useful to run the ‘autopopulate_main_menus’ command to populate main menus for your site(s).
However, this will only yield useful results if the ‘root page’ you’ve set for your site(s) is what you consider to be the ‘Home’ page, and the pages directly below that are the pages you’d like to link to in your main menu.
For example, if your page structure looked like the following:
Home (Set as 'root page' for the site) ├── About us ├── What we do ├── Careers | ├── Vacancy one | └── Vacancy two ├── News & events | ├── News | └── Events └── Contact us
Running the command from the console:
python manage.py autopopulate_main_menus
Would create a main menu with the following items:
- About us
- What we do
- Careers
- News & events
- Contact us
If you’d like wagtailmenus to also include a link to the ‘home page’, you can use the ‘–add-home-links’ option, like so:
python manage.py autopopulate_main_menus --add-home-links=True
This would create a main menu with the following items:
- Home
- About us
- What we do
- Careers
- News & events
- Contact us
Note
The ‘autopopulate_main_menus’ command is meant as ‘run once’ command to help you get started, and will only affect menus that do not already have any menu items defined. Running it more than once won’t have any effect, even if you make changes to your page tree before running it again.
Installing wagtail-condensedinlinepanel
¶
Although doing so is entirely optional, for an all-round better menu editing experience, we recommend using wagtailmenus together with wagtail-condensedinlinepanel.
Note
wagtail-condensedinlinepanel
hasn’t yet released a version compatible with Wagtail 2.0 (you’ll have to wait for version 0.5
that!). If you’re using an earlier version of Wagtail, the latest version of wagtail-condensedinlinepanel (0.4.2
) should work nicely for you.
wagtail-condensedinlinepanel
offers a React-powered alternative to Wagtail’s built-in InlinePanel
with some great extra features that make it perfect for managing menu items; including drag-and-drop reordering and the ability to add new items at any position.
If you’d like to give it a try, follow the installation instructions below, and wagtailmenus will automatically use the app’s CollapsedInlinePanel
class.
Install the package using pip:
pip install wagtail-condensedinlinepanel>=0.4
Add
condensedinlinepanel
to theINSTALLED_APPS
setting in your project settings:INSTALLED_APPS = [ ... 'condensedinlinepanel', ... ]
Note
If for some reason you want to use wagtail-condensedinlinepanel
for
other things, but would prefer NOT to use it for editing menus, you can
make wagtailmenus revert to using standard InlinePanel
by adding
WAGTAILMENUS_USE_CONDENSEDINLINEPANEL = False
to your project settings.
Managing main menus via the CMS¶
Log into the Wagtail CMS for your project (as a superuser).
Click on Settings in the side menu, then select Main menu from the options that appear.
You’ll be automatically redirected to the an edit page for the current site (or the ‘default’ site, if the current site cannot be identified). For multi-site projects, a ‘site switcher’ will appear in the top right, allowing you to edit main menus for each site.
Use the MENU ITEMS inline panel to define the root-level items. If you wish, you can use the handle field to specify an additional value for each item, which you’ll be able to access in a custom main menu template.
Note
Even if selected as menu items, pages must be ‘live’ and have a
show_in_menus
value ofTrue
in order to appear in menus. If you’re expecting to see new page links in a menu, but the pages are not showing up, edit the page and check whether the “Show in menus” checkbox is checked (found under the “Promote” tab by default).At the very bottom of the form, you’ll find the ADVANCED SETTINGS panel, which is collapsed by default. Click on the arrow icon next to the heading to reveal the Maximum levels and Specific usage fields, which you can alter to fit the needs of your project. For more information about specific usage see ‘Specific’ pages and menus.
Click on the Save button at the bottom of the page to save your changes.
Managing flat menus via the CMS¶
The flat menu list¶
All of the flat menus created for a project will appear in the menu list page making it easy to find, update, copy or delete your menus later. As soon as you create menus for more than one site in a multi-site project, the listing page will give you additional information and filters to help manage your menus:
To access the flat menu list, do the following:
- Log into the Wagtail CMS for your project (as a superuser).
- Click on “Settings” in the side menu, then on “Flat menus”.
Adding a new flat menu¶
From the listing page above, click the “Add flat menu” button
Fill out the form, choosing a unique-for-site “handle”, which you’ll use to reference the menu when using the
{% flat_menu %}
tag.Note
If you know in advance what menus you’re likely to have in your project, you can define some pre-set choices for the
handle
field using the WAGTAILMENUS_FLAT_MENUS_HANDLE_CHOICES setting. When used, thehandle
field will become a select field, saving you from having to enter values manually.Use the “MENU ITEMS” inline panel to define the links you want the menu to have. If you wish, you can use the handle field to specify an additional value for each item, which you’ll be able to access in from within menu templates.
Note
Even if selected as menu items, pages must be ‘live’ and have a
show_in_menus
value ofTrue
in order to appear in menus. If you’re expecting to see new page links in a menu, but the pages are not showing up, edit the page and check whether the “Show in menus” checkbox is checked (found under the “Promote” tab by default).At the very bottom of the form, you’ll find the “ADVANCED SETTINGS” panel, which is collapsed by default. Click on the arrow icon next to the heading to reveal the Maximum levels and Specific usage fields, which you can alter to fit the needs of your project. For more information about usage specific pages in menus, see ‘Specific’ pages and menus
Click on the Save button at the bottom of the page to save your changes.
Rendering menus¶
Template tags reference¶
The main_menu
tag¶
The main_menu
tag allows you to display the MainMenu
defined for the current site in your Wagtail project, with CSS classes automatically applied to each item to indicate the current page or ancestors of the current page. It also does a few sensible things, like never adding the ‘ancestor’ class for a homepage link, or outputting children for it.
Example usage¶
...
{% load menu_tags %}
...
{% main_menu max_levels=3 use_specific=USE_SPECIFIC_TOP_LEVEL template="menus/custom_main_menu.html" sub_menu_template="menus/custom_sub_menu.html" %}
...
Supported arguments¶
Required? | Expected value type | Default value |
---|---|---|
No | bool |
True |
Adding show_multiple_levels=False
to the tag in your template is essentially a more descriptive way of adding max_levels
to 1
.
Required? | Expected value type | Default value |
---|---|---|
No | int |
None |
Provide an integer value to override the max_levels
field value defined on your menu. Controls how many levels should be rendered (when show_multiple_levels
is True
).
Required? | Expected value type | Default value |
---|---|---|
No | int (see Supported values for fetching specific pages) |
None |
Provide a value to override the use_specific
field value defined on your main menu. Allows you to control how wagtailmenus makes use of PageQuerySet.specific()
and Page.specific
when rendering the menu. For more information and examples, see: Using the use_specific template tag argument.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
True |
Item repetition settings set on each page are respected by default, but you can add allow_repeating_parents=False
to ignore them, and not repeat any pages in sub-menus when rendering multiple levels.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
True |
The tag will attempt to add ‘active’ and ‘ancestor’ CSS classes to the menu items (where applicable) to indicate the active page and ancestors of that page. To disable this behaviour, add apply_active_classes=False
to the tag in your template.
You can change the CSS class strings used to indicate ‘active’ and ‘ancestor’ statuses by utilising the WAGTAILMENUS_ACTIVE_CLASS and WAGTAILMENUS_ACTIVE_ANCESTOR_CLASS settings.
Required? | Expected value type | Default value |
---|---|---|
No | Template path (str ) |
'' |
Lets you render the menu to a template of your choosing. If not provided, wagtailmenus will attempt to find a suitable template automatically.
For more information about overriding templates, see: Using your own menu templates.
For a list of preferred template paths this tag, see: Preferred template paths for {% main_menu %}.
Required? | Expected value type | Default value |
---|---|---|
No | Template path (str ) |
'' |
Lets you specify a template to be used for rendering sub menus. All subsequent calls to {% sub_menu %}
within the context of the section menu will use this template unless overridden by providing a template
value to {% sub_menu %}
in a menu template. If not provided, wagtailmenus will attempt to find a suitable template automatically.
For more information about overriding templates, see: Using your own menu templates.
For a list of preferred template paths this tag, see: Preferred template paths for {% main_menu %}.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
False |
By default, relative page URLs are used for the href
attribute on page links when rendering your menu. If you wish to use absolute page URLs instead, add use_absolute_page_urls=True
to the main_menu
tag in your template. The preference will also be respected automatically by any subsequent calls to {% sub_menu %}
during the course of rendering the menu (unless explicitly overridden in custom menu templates ).
The flat_menu
tag¶
Example usage¶
...
{% load menu_tags %}
...
{% flat_menu 'footer' max_levels=1 show_menu_heading=False use_specific=USE_SPECIFIC_TOP_LEVEL fall_back_to_default_site_menus=True %}
...
Supported arguments¶
Required? | Expected value type | Default value |
---|---|---|
Yes | str |
None |
The unique handle for the flat menu you want to render, e.g. 'info'
,
'contact'
, or 'services'
. You don’t need to include the handle
key if supplying as the first argument to the tag (you can just do {% flat_menu 'menu_handle' %}
).
Required? | Expected value type | Default value |
---|---|---|
No | bool |
True |
Passed through to the template used for rendering, where it can be used to conditionally display a heading above the menu.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
True |
Flat menus are designed for outputting simple, flat lists of links. But, you can alter the max_levels
field value on your FlatMenu
objects in the CMS to enable multi-level output for specific menus. If you want to absolutely never show anything but the MenuItem
objects defined on the menu, you can override this behaviour by adding show_multiple_levels=False
to the tag in your template.
Required? | Expected value type | Default value |
---|---|---|
No | int |
None |
Provide an integer value to override the max_levels
field value defined on your menu. Controls how many levels should be rendered (when show_multiple_levels
is True
).
Required? | Expected value type | Default value |
---|---|---|
No | int (see Supported values for fetching specific pages) |
None |
Provide a value to override the use_specific
field value defined on your flat menu. Allows you to control how wagtailmenus makes use of PageQuerySet.specific()
and Page.specific
when rendering the menu.
For more information and examples, see: Using the use_specific template tag argument.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
False |
Unlike main_menu
and section_menu
, flat_menu
will NOT attempt to add 'active'
and 'ancestor'
classes to the menu items by default, as this is often not useful. You can override this by adding apply_active_classes=true
to the tag in your template.
You can change the CSS class strings used to indicate ‘active’ and ‘ancestor’ statuses by utilising the WAGTAILMENUS_ACTIVE_CLASS and WAGTAILMENUS_ACTIVE_ANCESTOR_CLASS settings.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
True |
Repetition-related settings on your pages are respected by default, but you can add allow_repeating_parents=False
to ignore them, and not repeat any pages in sub-menus when rendering. Please note that using this option will only have an effect if use_specific
has a value of 1
or higher.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
False |
When using the flat_menu
tag, wagtailmenus identifies the ‘current site’, and attempts to find a menu for that site, matching the handle
provided. By default, if no menu is found for the current site, nothing is rendered. However, if fall_back_to_default_site_menus=True
is provided, wagtailmenus will search search the ‘default’ site (In the CMS, this will be the site with the ‘Is default site’ checkbox ticked) for a menu with the same handle, and use that instead before giving up.
The default value can be changed to True
by utilising the WAGTAILMENUS_FLAT_MENUS_FALL_BACK_TO_DEFAULT_SITE_MENUS setting.
Required? | Expected value type | Default value |
---|---|---|
No | Template path (str ) |
'' |
Lets you render the menu to a template of your choosing. If not provided, wagtailmenus will attempt to find a suitable template automatically.
For more information about overriding templates, see: Using your own menu templates.
For a list of preferred template paths this tag, see: Preferred template paths for {% flat_menu %}.
Required? | Expected value type | Default value |
---|---|---|
No | Template path (str ) |
'' |
Lets you specify a template to be used for rendering sub menus (if enabled using show_multiple_levels
). All subsequent calls to {% sub_menu %}
within the context of the flat menu will use this template unless overridden by providing a template
value to {% sub_menu %}
directly in a menu template. If not provided, wagtailmenus will attempt to find a suitable template automatically.
For more information about overriding templates, see: Using your own menu templates.
For a list of preferred template paths this tag, see: Preferred template paths for {% flat_menu %}.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
False |
By default, relative page URLs are used for the href
attribute on page links when rendering your menu. If you wish to use absolute page URLs instead, add use_absolute_page_urls=True
to the {% flat_menu %}
tag in your template. The preference will also be respected automatically by any subsequent calls to {% sub_menu %}
during the course of rendering the menu (unless explicitly overridden in custom menu templates).
The section_menu
tag¶
The section_menu
tag allows you to display a context-aware, page-driven menu in your project’s templates, with CSS classes automatically applied to each item to indicate the active page or ancestors of the active page.
Example usage¶
...
{% load menu_tags %}
...
{% section_menu max_levels=3 use_specific=USE_SPECIFIC_OFF template="menus/custom_section_menu.html" sub_menu_template="menus/custom_section_sub_menu.html" %}
...
Supported arguments¶
Required? | Expected value type | Default value |
---|---|---|
No | bool |
True |
Passed through to the template used for rendering, where it can be used to conditionally display the root page of the current section.
Required? | Expected value type | Default value |
---|---|---|
No | int |
2 |
Lets you control how many levels of pages should be rendered (the section root page does not count as a level, just the first set of pages below it). If you only want to display the first level of pages below the section root page (whether pages linked to have children or not), add max_levels=1
to the tag in your template. You can display additional levels by providing a higher value.
The default value can be changed by utilising the WAGTAILMENUS_DEFAULT_SECTION_MENU_MAX_LEVELS setting.
Required? | Expected value type | Default value |
---|---|---|
No | int (see Supported values for fetching specific pages) |
1 (Auto) |
Allows you to control how wagtailmenus makes use of PageQuerySet.specific()
and Page.specific
when rendering the menu, helping you to find the right balance between functionality and performance.
For more information and examples, see: Using the use_specific template tag argument.
The default value can be altered by utilising the WAGTAILMENUS_DEFAULT_SECTION_MENU_USE_SPECIFIC setting.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
True |
Adding show_multiple_levels=False
to the tag in your template essentially overrides max_levels
to 1
. It’s just a little more descriptive.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
True |
The tag will add ‘active’ and ‘ancestor’ classes to the menu items where applicable, to indicate the active page and ancestors of that page. To disable this behaviour, add apply_active_classes=False
to the tag in your template.
You can change the CSS class strings used to indicate ‘active’ and ‘ancestor’ statuses by utilising the WAGTAILMENUS_ACTIVE_CLASS and WAGTAILMENUS_ACTIVE_ANCESTOR_CLASS settings.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
True |
Repetition-related settings on your pages are respected by default, but you can add allow_repeating_parents=False
to ignore them, and not repeat any pages in sub-menus when rendering. Please note that using this option will only have an effect if use_specific
has a value of 1
or higher.
Required? | Expected value type | Default value |
---|---|---|
No | Template path (str ) |
'' |
Lets you render the menu to a template of your choosing. If not provided, wagtailmenus will attempt to find a suitable template automatically.
For more information about overriding templates, see: Using your own menu templates.
For a list of preferred template paths this tag, see: Preferred template paths for {% section_menu %}.
Required? | Expected value type | Default value |
---|---|---|
No | Template path (str ) |
'' |
Lets you specify a template to be used for rendering sub menus. All subsequent calls to {% sub_menu %}
within the context of the section menu will use this template unless overridden by providing a template
value to {% sub_menu %}
in a menu template. If not provided, wagtailmenus will attempt to find a suitable template automatically.
For more information about overriding templates, see: Using your own menu templates.
For a list of preferred template paths this tag, see: Preferred template paths for {% section_menu %}.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
False |
By default, relative page URLs are used for the href
attribute on page links when rendering your menu. If you wish to use absolute page URLs instead, add use_absolute_page_urls=True
to the {% section_menu %}
tag in your template. The preference will also be respected automatically by any subsequent calls to {% sub_menu %}
during the course of rendering the menu (unless explicitly overridden in custom menu templates).
The children_menu
tag¶
The children_menu
tag can be used in page templates to display a menu of children of the current page. You can also use the parent_page argument to show children of a different page.
Example usage¶
...
{% load menu_tags %}
...
{% children_menu some_other_page max_levels=2 use_specific=USE_SPECIFIC_OFF template="menus/custom_children_menu.html" sub_menu_template="menus/custom_children_sub_menu.html" %}
...
Supported arguments¶
Required? | Expected value type | Default value |
---|---|---|
No | A Page object |
None |
Allows you to specify a page to output children for. If no alternate page is specified, the tag will automatically use self
from the context to render children pages for the current/active page.
Required? | Expected value type | Default value |
---|---|---|
No | int |
1 |
Allows you to specify how many levels of pages should be rendered. For example, if you want to display the direct children pages and their children too, add max_levels=2
to the tag in your template.
The default value can be changed by utilising the WAGTAILMENUS_DEFAULT_CHILDREN_MENU_MAX_LEVELS setting.
Required? | Expected value type | Default value |
---|---|---|
No | int (see Supported values for fetching specific pages) |
1 (Auto) |
Allows you to specify how wagtailmenus makes use of PageQuerySet.specific()
and Page.specific
when rendering the menu.
For more information and examples, see: Using the use_specific template tag argument.
The default value can be altered by adding a WAGTAILMENUS_DEFAULT_CHILDREN_MENU_USE_SPECIFIC setting to your project’s settings.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
False |
Unlike main_menu
and section_menu`, children_menu
will NOT attempt to add 'active'
and 'ancestor'
classes to the menu items by default, as this is often not useful. You can override this by adding apply_active_classes=true
to the tag in your template.
You can change the CSS class strings used to indicate ‘active’ and ‘ancestor’ statuses by utilising the WAGTAILMENUS_ACTIVE_CLASS and WAGTAILMENUS_ACTIVE_ANCESTOR_CLASS settings.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
True |
Repetition-related settings on your pages are respected by default, but you can add allow_repeating_parents=False
to ignore them, and not repeat any pages in sub-menus when rendering. Please note that using this option will only have an effect if use_specific
has a value of 1
or higher.
Required? | Expected value type | Default value |
---|---|---|
No | Template path (str ) |
'' |
Lets you render the menu to a template of your choosing. If not provided, wagtailmenus will attempt to find a suitable template automatically (see below for more details).
For more information about overriding templates, see: Using your own menu templates
For a list of preferred template paths this tag, see: Preferred template paths for {% children_menu %}
Required? | Expected value type | Default value |
---|---|---|
No | Template path (str ) |
'' |
Lets you specify a template to be used for rendering sub menus. All subsequent calls to {% sub_menu %}
within the context of the section menu will use this template unless overridden by providing a template
value to {% sub_menu %}
in a menu template. If not provided, wagtailmenus will attempt to find a suitable template automatically
For more information about overriding templates, see: Using your own menu templates
For a list of preferred template paths this tag, see: Preferred template paths for {% children_menu %}
Required? | Expected value type | Default value |
---|---|---|
No | bool |
False |
By default, relative page URLs are used for the href
attribute on page links when rendering your menu. If you wish to use absolute page URLs instead, add use_absolute_page_urls=True
to the {% children_menu %}
tag in your template. The preference will also be respected automatically by any subsequent calls to {% sub_menu %}
during the course of rendering the menu (unless explicitly overridden in custom menu templates).
The sub_menu
tag¶
The sub_menu
tag is used within menu templates to render additional levels of pages within a menu. It’s designed to pick up on variables added to the context by the other menu tags, and so can behave a little unpredictably if called directly, without those context variables having been set. It requires only one parameter to work, which is menuitem_or_page
.
Example usage¶
...
{% load menu_tags %}
...
{% for item in menu_items %}
<li class="{{ item.active_class }}">
<a href="{{ item.href }}">{{ item.text }}</a>
{% if item.has_children_in_menu %}
{% sub_menu item %}
{% endif %}
</li>
{% endfor %}
...
Supported arguments¶
Required? | Expected value type | Default value |
---|---|---|
Yes | An item from the menu_items list |
None (inherit from original tag) |
When iterating through a list of menu_items
within a menu template, the current
item must be passed to {% sub_menu %}
so that it knows which page to render a sub-menu for. You don’t need to include the menuitem_or_page
key if supplying the value as the first argument to the tag (you can just do {% sub_menu item %}
).
Required? | Expected value type | Default value |
---|---|---|
No | bool |
None (inherit from original tag) |
Allows you to override the value set by the original tag by adding an alternative value to the {% sub_menu %}
tag in a custom menu template.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
None (inherit from original tag) |
Allows you to override the value set by the original tag by adding an alternative value to the {% sub_menu %}
tag in a custom menu template.
Required? | Expected value type | Default value |
---|---|---|
No | Template path (str ) |
'' |
Allows you to override the template set by the original menu tag (sub_menu_template
in the context) by passing a fixed template path to the {% sub_menu %}
tag in a custom menu template.
For more information about overriding templates, see: Using your own menu templates
Required? | Expected value type | Default value |
---|---|---|
No | int (see Supported values for fetching specific pages) |
None |
Allows you to override the value set on the original tag by adding an alternative value to the {% sub_menu %}
tag in a custom menu template.
For more information and examples, see: Using the use_specific template tag argument.
Required? | Expected value type | Default value |
---|---|---|
No | bool |
None (inherit from original tag) |
Allows you to override the value set on the original tag by explicitly adding use_absolute_page_urls=True
or use_absolute_page_urls=False
to a {% sub_menu %}
tag in a custom menu template.
If True
, absolute page URLs will be used for the href
attributes on page links instead of relative URLs.
Using your own menu templates¶
Writing custom menu templates¶
What context variables are available to use?¶
The following variables are added to the context by all included template tags, which you can make use of in your templates:
menu_items : | A list of For more details on the attribute values added by wagtailmenus, see: Attributes added to each item in menu_items. |
---|---|
current_level : | An integer indicating the current level being rendered. This starts at
|
max_levels : | An integer indicating the maximum number of levels that should be rendered
for the current menu, as determined by the original |
current_template : | |
The name of the template currently being used for rendering. This is most
useful when rendering a |
|
sub_menu_template : | |
The name of the template that should be used for rendering any further
levels (should be picked up automatically by the |
|
original_menu_tag : | |
A string value indicating the name of tag was originally called in order to
render the branch currently being rendered. The value will be one of
|
|
allow_repeating_parents : | |
A boolean indicating whether |
|
apply_active_classes : | |
A boolean indicating whether |
|
use_absolute_page_urls : | |
A boolean indicating whether absolute page URLs should be used for page links when rendering. |
Attributes added to each item in menu_items
¶
href : | The URL that the menu item should link to. |
---|---|
text : | The text that should be used for the menu item. You can change the field or attribute used to populate the |
active_class : | A class name to indicate the ‘active’ state of the menu item. The value will be ‘active’ if linking to the current page, or ‘ancestor’ if linking to one of it’s ancestors. You can change the CSS class strings used to indicate ‘active’ and ‘ancestor’ statuses by utilising the WAGTAILMENUS_ACTIVE_CLASS and WAGTAILMENUS_ACTIVE_ANCESTOR_CLASS settings. |
has_children_in_menu : | |
A boolean indicating whether the menu item has children that should be output as a sub-menu. |
Getting wagtailmenus to use your custom menu templates¶
Using preferred paths and names for your templates¶
This is the easiest (and recommended) approach for getting wagtailmenus to use your custom menu templates for rendering.
When use don’t use template
or sub_menu_template
arguments to explicitly specify templates for each tag, wagtailmenus looks in a list of gradually less specific paths for templates to use. If you’re familiar with Django, you’ll probably already be familiar with this approach. Essentially, you can override existing menu templates or add custom ones simply by putting them at a preferred location within your project.
If you have multi-site project, and want to be able to use different templates for some or all of those sites, wagtailmenus can be configured to look for additional ‘site specific’ paths for each template. To enable this feature, you need to add the following to your project’s settings:
WAGTAILMENUS_SITE_SPECIFIC_TEMPLATE_DIRS = True
With this set, tags will look for a request
value in the context, and try to identify the current site being viewed by looking for a site
attribute on request
(which is set by wagtail.wagtailcore.middleware.SiteMiddleware
). It then uses the domain
field from that Site
object to look for templates with that domain name included.
The following sections outline the preferred path locations for each tag, in the order that they are searched (most specific first).
{% main_menu %}
¶Note
Template paths marked with an asterisk (*) are only included if you’ve set the WAGTAILMENUS_SITE_SPECIFIC_TEMPLATE_DIRS setting to True
in your project settings. They are not used by default.
For the menu itself:
"menus/{{ request.site.domain }}/main/menu.html"
*"menus/{{ request.site.domain }}/main_menu.html"
*"menus/main/menu.html"
"menus/main_menu.html"
For any sub-menus:
"menus/{{ request.site.domain }}/sub_menu.html"
*"menus/{{ request.site.domain }}/main_sub_menu.html"
*"menus/{{ request.site.domain }}/sub_menu.html"
*"menus/main/sub_menu.html"
"menus/main_sub_menu.html"
"menus/sub_menu.html"
{% flat_menu %}
¶For flat menus, the tag also uses the handle field of the specific menu being rendered, so that you can have wagtailmenus use different templates for different menus.
Note
Template paths marked with an asterisk (*) are only included if you’ve set the WAGTAILMENUS_SITE_SPECIFIC_TEMPLATE_DIRS setting to True
in your project settings. They are not used by default.
For the menu itself:
"menus/{{ request.site.domain }}/flat/{{ menu.handle }}/menu.html"
*"menus/{{ request.site.domain }}/flat/{{ menu.handle }}.html"
*"menus/{{ request.site.domain }}/{{ menu.handle }}/menu.html"
*"menus/{{ request.site.domain }}/{{ menu.handle }}.html"
*"menus/{{ request.site.domain }}/flat/menu.html"
*"menus/{{ request.site.domain }}/flat/default.html"
*"menus/{{ request.site.domain }}/flat_menu.html"
*"menus/flat/{{ menu.handle }}/menu.html"
"menus/flat/{{ menu.handle }}.html"
"menus/{{ menu.handle }}/menu.html"
"menus/{{ menu.handle }}.html"
"menus/flat/default.html"
"menus/flat/menu.html"
"menus/flat_menu.html"
For any sub-menus:
"menus/{{ request.site.domain }}/flat/{{ menu.handle }}/sub_menu.html"
*"menus/{{ request.site.domain }}/flat/{{ menu.handle }}_sub_menu.html"
*"menus/{{ request.site.domain }}/{{ menu.handle }}/sub_menu.html"
*"menus/{{ request.site.domain }}/{{ menu.handle }}_sub_menu.html"
*"menus/{{ request.site.domain }}/flat/sub_menu.html"
*"menus/{{ request.site.domain }}/sub_menu.html"
*"menus/flat/{{ menu.handle }}/sub_menu.html"
"menus/flat/{{ menu.handle }}_sub_menu.html"
"menus/{{ menu.handle }}/sub_menu.html"
"menus/{{ menu.handle }}_sub_menu.html"
"menus/flat/sub_menu.html"
"menus/sub_menu.html"
{% section_menu %}
¶Note
Template paths marked with an asterisk (*) are only included if you’ve set the WAGTAILMENUS_SITE_SPECIFIC_TEMPLATE_DIRS setting to True
in your project settings. They are not used by default.
For the menu itself:
"menus/{{ request.site.domain }}/section/menu.html"
*"menus/{{ request.site.domain }}/section_menu.html"
*"menus/section/menu.html"
"menus/section_menu.html"
For any sub-menus:
"menus/{{ request.site.domain }}/section/sub_menu.html"
*"menus/{{ request.site.domain }}/section_sub_menu.html"
*"menus/{{ request.site.domain }}/sub_menu.html"
*"menus/section/sub_menu.html"
"menus/section_sub_menu.html"
"menus/sub_menu.html"
{% children_menu %}
¶Note
Template paths marked with an asterisk (*) are only included if you’ve set the WAGTAILMENUS_SITE_SPECIFIC_TEMPLATE_DIRS setting to True
in your project settings. They are not used by default.
For the menu itself:
"menus/{{ request.site.domain }}/children/menu.html"
*"menus/{{ request.site.domain }}/children_menu.html"
*"menus/children/menu.html"
"menus/children_menu.html"
For any sub-menus:
"menus/{{ request.site.domain }}/children/sub_menu.html"
*"menus/{{ request.site.domain }}/children_sub_menu.html"
*"menus/{{ request.site.domain }}/sub_menu.html"
*"menus/children/sub_menu.html"
"menus/children_sub_menu.html"
"menus/sub_menu.html"
Specifying menu templates using template tag parameters¶
All template tags included in wagtailmenus support template
and sub_menu_template
arguments to allow you to explicitly override the templates used during rendering.
For example, if you had created the following templates in your project’s root ‘templates’ directory:
"templates/custom_menus/main_menu.html"
"templates/custom_menus/main_menu_sub_menu.html"
You could make The main_menu tag use those templates for rendering by specifying them in your template, like so:
{% main_menu max_levels=2 template="custom_menus/main_menu.html" sub_menu_template="templates/custom_menus/main_menu_sub_menu.html" %}
Or you could just override one or the other (you don’t have to override both). e.g:
{# Just override the template for the top-level #}
{% main_menu max_levels=2 template="custom_menus/main_menu.html" %}
{# Just override the template used for sub-menus #}
{% main_menu max_levels=2 sub_menu_template="custom_menus/main_menu.html" %}
The MenuPage
and MenuPageMixin
models¶
The MenuPageMixin
and MenuPage
models were created specifically to solve the problem of important page links becoming merely toggles in multi-level menus, preventing users from accessing them easily.
A typical scenario¶
Let’s say you have an About Us section on your site. The top-level “About Us” page has content on it that is just as important as it’s children (e.g. “Meet the team”, “Our mission and values”, “Staff vacancies”). Because of this, you’d like visitors to be able to access the root page as easily as those pages. But, your site uses some form of collapsible multi-level navigation, and the About Us page link has become merely a toggle for hiding and showing its sub-pages, making it difficult to get to directly:

MenuPage
to the rescue!¶
If the About Us page uses a model that subclasses MenuPage
or MenuPageMixin
, you can solve this issue by doing the following:
Edit the page via the CMS, and click on the “Settings” tab.
Uncollapse the “ADVANCED MENU BEHAVIOUR” panel by clicking the downward-pointing arrow next to the panel’s label
Tick the Repeat in sub-navigation checkbox that appears, and publish your changes.
Now, wherever the children of the About Us page are output (using one of the above menu tags), an additional link will appear alongside them, allowing the that page to be accessed more easily. In the example above, you’ll see “Section overview” has been added to the a Repeated item link text field. With this set, the link text for the repeated item should read “Section overview”, instead of just repeating the page’s title, like so:

The menu tags do some extra work to ensure both links are never assigned the ‘active’ class. When on the ‘About Us’ page, the tags will treat the repeated item as the ‘active’ page, and just assign the ‘ancestor’ class to the original, so that the styling is consistent with other page links rendered at that level.
Implementing MenuPage
into your project¶
Subclass
wagtailmenus.models.MenuPage
on your model instead of the usualwagtail.wagtacore.models.Page
, just like in the following example:# appname/models.py from wagtailmenus.models import MenuPage class GenericPage(MenuPage): """ This model will gain the fields, methods and 'setting_panels' attribute from MenuPage. """ ...
Or, if you’re using an abstract ‘base’ model in you project to improve consistency of common functionality, you could update the base model, like so:
# appname/models.py from wagtailmenus.models import MenuPage class BaseProjectPage(MenuPage): ... class GenericPage(BaseProjectPage): ... class ContactPage(BaseProjectPage): ...
If you’re not overriding the
settings_panels
attribute on any of the models involved, you can skip this step. But, if you are overriding thesettings_panels
attribute on a custom model to surface other custom fields in that tab, you’ll need to include additional panels to surface the newMenuPage
fields in the page edit interface. Wagtailmenus includes a pre-definedmenupage_panel
to make this easier, which you can use like this:# appname/models.py from wagtailmenus.models import MenuPage from wagtailmenus.panels import menupage_panel class GenericPage(MenuPage): """ This model will gain the fields, methods and `setting_panels` attribute from `MenuPage`, but `settings_panels` is being overridden to include other fields in the `Settings` tab. """ custom_settings_field_one = BooleanField(default=False) custom_settings_field_two = BooleanField(default=True) # 'menupage_panel' is a collapsible `MultiFieldPanel` with the important # fields already grouped together, making it easy to include in custom # panel definitions, like so: settings_panels = [ FieldPanel('custom_settings_field_one'), FieldPanel('custom_settings_field_two'), menupage_panel ] ...
Create migtations for any models you’ve updated by running:
python manage.py makemigrations appname
Apply the new migrations by running:
python manage.py migrate appname
Implementing MenuPageMixin
into your project¶
Wagtail has a restriction that forbids models from subclassing more than one other class derived from Page
, and that single page-derived class must be the left-most item when subclassing more than one model class. Most of the time, that doesn’t cause any noticeable issues. But, in some cases, it can make it difficult to swap out base model classes used for page models. In these cases, you can use wagtailmenus.models.MenuPageMixin
instead of MenuPage
.
Note
MenuPageMixin
doesn’t change make any changes to the panel configuration on your model in order to surface it’s new fields in the page editing interface. If you want those fields to appear, you’ll have to override settings_panels
on your model to include menupage_panel
- Subclass
wagtailmenus.models.MenuPageMixin
to create your model, including it to the right of any other class that subclassesPage
:
# appname/models.py from wagtail.wagtailforms.models import AbstractEmailForm from wagtailmenus.models import MenuPageMixin from wagtailmenus.panels import menupage_panel class MyEmailFormPage(AbstractEmailForm, MenuPageMixin): """This page will gain the same fields and methods as if it extended `wagtailmenus.models.MenuPage`""" ... # It's not possible for MenuPageMixin to set `settings_panel`, so you must # override `settings_panels` yourself, and include `menupage_panel` in # order to surface additional field in the 'Settings' tab of the editor # interface settings_panels = [ FieldPanel('custom_settings_field_one'), FieldPanel('custom_settings_field_two'), menupage_panel ] ...
- Create migtations for any models you’ve updated by running:
python manage.py makemigrations appname
- Apply the new migrations by running:
python manage.py migrate appname
Using MenuPage
to manipulating sub-menu items¶
When a page model subclasses MenuPage
or MenuPageMixin
, pages of that type are given special treatment by the menu generation template tags included in wagtailmenus, allowing them to make changes to the sub-menu items that get rendered below them.
The functionaliy exists to allow MenuPage
pages to add repeating links to themselves into a sub-menu, but can be extended to meet any custom needs you might have.
For example, if you had a ContactPage
model, and in main menus, you wanted to add some additional links below each ContactPage
, you could achieve that by overriding the modify_submenu_items()
and has_submenu_items()
methods like so:
# appname/models.py
from wagtailmenus.models import MenuPage
class ContactPage(MenuPage):
...
current_page, current_ancestor_ids,
current_site, allow_repeating_parents, apply_active_classes,
original_menu_tag, menu_instance, request, use_absolute_page_urls
def modify_submenu_items(self, menu_items, **kwargs):
"""
If rendering a 'main_menu', add some additional menu items to the end
of the list that link to various anchored sections on the same page.
We're only making use 'original_menu_tag' and 'current_site' in this
example, but `kwargs` should have all of the following keys:
* 'current_page'
* 'current_ancestor_ids'
* 'current_site'
* 'allow_repeating_parents'
* 'apply_active_classes'
* 'original_menu_tag'
* 'menu_instance'
* 'request'
* 'use_absolute_page_urls'
"""
# Start by applying default modifications
menu_items = super(ContactPage, self).modify_submenu_items(menu_items, **kwargs)
if kwargs['original_menu_tag'] == 'main_menu':
base_url = self.relative_url(kwargs['current_site'])
"""
Additional menu items can be objects with the necessary attributes,
or simple dictionaries. `href` is used for the link URL, and `text`
is the text displayed for each link. Below, I've also used
`active_class` to add some additional CSS classes to these items,
so that I can target them with additional CSS
"""
menu_items.extend((
{
'text': 'Get support',
'href': base_url + '#support',
'active_class': 'support',
},
{
'text': 'Speak to someone',
'href': base_url + '#call',
'active_class': 'call',
},
{
'text': 'Map & directions',
'href': base_url + '#map',
'active_class': 'map',
},
))
return menu_items
def has_submenu_items(self, **kwargs):
"""
Because `modify_submenu_items` is being used to add additional menu
items, we need to indicate in menu templates that `ContactPage` objects
do have submenu items in main menus, even if they don't have children
pages.
We're only making use 'original_menu_tag' in this example, but
`kwargs` should have all of the following keys:
* 'current_page'
* 'allow_repeating_parents'
* 'original_menu_tag'
* 'menu_instance'
* 'request'
"""
if kwargs['original_menu_tag'] == 'main_menu':
return True
# Resort to default behaviour
return super(ContactPage, self).has_submenu_items(**kwargs)
The above changes would result in the following HTML output when rendering a ContactPage
instance in a main menu:
...
<li class=" dropdown">
<a href="/contact-us/" class="dropdown-toggle" id="ddtoggle_18" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Contact us <span class="caret"></span></a>
<ul class="dropdown-menu" aria-labelledby="ddtoggle_18">
<li class="support"><a href="/contact-us/#support">Get support</a></li>
<li class="call"><a href="/contact-us/#call">Speak to someone</a></li>
<li class="map"><a href="/contact-us/#map">Map & directions</a></li>
</ul>
</li>
...
You can also modify sub-menu items based on field values for specific instances, rather than doing the same for every page of the same type. Here’s another example:
# appname/models.py
from django.db import models
from wagtailmenus.models import MenuPage
class SectionRootPage(MenuPage):
add_submenu_item_for_news = models.BooleanField(default=False)
def modify_submenu_items(
self, menu_items, current_page, current_ancestor_ids, current_site,
allow_repeating_parents, apply_active_classes, original_menu_tag='',
menu_instance, request, use_absolute_page_urls
):
menu_items = super(SectionRootPage,self).modify_menu_items(
menu_items, current_page, current_ancestor_ids,
current_site, allow_repeating_parents, apply_active_classes,
original_menu_tag, menu_instance, request, use_absolute_page_urls)
if self.add_submenu_item_for_news:
menu_items.append({
'href': '/news/',
'text': 'Read the news',
'active_class': 'news-link',
})
return menu_items
def has_submenu_items(
self, current_page, allow_repeating_parents, original_menu_tag,
menu_instance, request
):
if self.add_submenu_item_for_news:
return True
return super(SectionRootPage, self).has_submenu_items(
current_page, allow_repeating_parents, original_menu_tag,
menu_instance, request)
Note
If you’re overriding modify_submenu_items()
, please ensure that ‘repeated menu items’ are still added as the first item in the returned menu_items
list. If not, active class highlighting might not work as expected.
The AbstractLinkPage
model¶
Because main and flat menus only allow editors to define the top-level items in a menu, the AbstractLinkPage
model was introduced to give them a way to easily add additional links to menus, by adding additional pages to the page tree.
Just like menu items defined for a menu via the CMS, link pages can link to other pages or custom URLs, and if linking to another page, the link will automatically become hidden if the target page is unpublished, expires, or is set to no longer show in menus. It will also appear again if the target page is published or set to show in menus again.
By default, link pages are not allowed to have children pages, and shouldn’t appear in wagtail-generated sitemaps or search results.
Implementing AbstractLinkPage
into your project¶
Like MenuPage
, AbstractLinkPage
is an abstract model, so in order to use it in your project, you need to subclass it.
- Subclass
AbstractLinkPage
to create a new page type model in your project:
# appname/models.py from wagtailmenus.models import AbstractLinkPage class LinkPage(AbstractLinkPage): pass
- Create migtations for any models you’ve updated by running:
python manage.py makemigrations appname
- Apply the new migrations by running:
python manage.py migrate appname
Advanced topics¶
‘Specific’ pages and menus¶
For pages, Wagtail makes use of a technique in Django called ‘multi-table inheritance’. In simple terms, this means that when you create an instance of a custom page type model, the data is saved in two different database tables:
- All of the standard fields from Wagtail’s
Page
model are stored in one table - Any data for additional fields specific to your custom model are saved in another one
Because of this, in order for Django to return ‘specific’ page type instance (e.g. an EventPage), it needs to fetch and join data from both tables; which has a negative effect on performance.
Menu generation is particularly resource intensive, because a menu needs to know a lot of data about a lot of pages. Add a need for ‘specific’ page instances to that mix (perhaps you need to access multlingual field values that only exist in the specific database table, or you want to use other custom field values in your menu templates), and that intensity is understandably greater, as the data will likely be spread over many tables (depending on how many custom page types you are using), needing lots of database joins to put everything together.
Because every project has different needs, wagtailmenus gives you some fine grained control over how ‘specific’ pages should be used in your menus. When defining a MainMenu
or FlatMenu
in the CMS, the Specific page use field allows you to choose one of the following options, which can also be passed to any of the included template tags using the use_specific
parameter.
Supported values for fetching specific pages¶
Off (value:
0
): Use only standardPage
model data and methods, and make the minimum number of database methods when rendering. If you aren’t using wagtailmenu’sMenuPage
model in your project, and don’t need to access any custom page model fields or methods in you menu templates, and aren’t overridingget_url_parts()
or otherPage
methods concerned with URL generation, you should use this option for optimal performance.Auto (value:
1
): Only fetch and use specific pages when needed forMenuPage
operations (e.g. for ‘repeating menu item’ behaviour, and manipulation of sub-menu items viahas_submenu_items()
andmodify_submenu_items()
methods).Top level (value:
2
): Only fetch and return specific page instances for the top-level menu items (pages that were manually added as menu items via the CMS), but only use vanillaPage
objects for any additional levels.Note
Although accepted by all menu tags, using use_specific=2 will only really effect
main_menu
andflat_menu
tags. All other tags will behave the same as if you’d supplied a value of Auto (1
).Always (value:
3
): Fetch and return specific page instances for ALL pages, so that custom page-type data and methods can be accessed in all menu templates. If you have a multilingual site and want to output translated page content in menus, or if you have models that overrideget_url_parts()
,relative_url()
or otherPage
methods involved in returning URLs, this is the option you should use in order to fetch the data as efficiently as possible.
Using the use_specific
template tag argument¶
All of the template tags included in wagtailmenus accept a use_specific
argument, allowing you to override any default settings, or the settings applied via the CMS to individual MainMenu
and FlatMenu
objects. As a value, you can pass in the integer value of any of the above options, for example:
...
{% main_menu use_specific=2 %}
...
{% section_menu use_specific=3 %}
...
{% children_menu use_specific=1 %}
Or, the following variables should be available in the context for you to use instead:
USE_SPECIFIC_OFF
(value:0
)USE_SPECIFIC_AUTO
(value1
)USE_SPECIFIC_TOP_LEVEL
(value2
)USE_SPECIFIC_ALWAYS
(value3
)
For example:
...
{% main_menu use_specific=USE_SPECIFIC_TOP_LEVEL %}
...
{% section_menu use_specific=USE_SPECIFIC_ALWAYS %}
...
{% children_menu use_specific=USE_SPECIFIC_AUTO %}
Using hooks to modify menus¶
On loading, Wagtail will search for any app with the file wagtail_hooks.py
and execute the contents. This provides a way to register your own functions to execute at certain points in Wagtail’s execution, such as when a Page
object is saved or when the main menu is constructed.
Registering functions with a Wagtail hook is done through the @hooks.register
decorator:
from wagtail.wagtailcore import hooks
@hooks.register('name_of_hook')
def my_hook_function(arg1, arg2...)
# your code here
Alternatively, hooks.register
can be called as an ordinary function, passing in the name of the hook and a handler function defined elsewhere:
hooks.register('name_of_hook', my_hook_function)
Wagtailmenus utilises this same ‘hooks’ mechanism to allow you make modifications to menus at certain points during the rendering process.
Hooks for modifying QuerySets¶
When a menu instance is gathering the data it needs to render itself, it typically uses one or more QuerySets to fetch Page
and MenuItem
data from the database. These hooks allow you to modify those QuerySets before they are evaluated, allowing you to efficiently control menu contents.
If you need to override a lot of menu class behaviour, and you’re comfortable with the idea of subclassing the existing classes and models to override the necessary methods, you might want to look at Using custom menu classes and models. But, if all you want to do is change the result of a menu’s get_base_page_queryset()
or get_base_menuitem_queryset()
(say, to limit the links that appear based on the permissions of the currently logged-in user), you may find it quicker & easier to use the following hooks instead.
menus_modify_base_page_queryset¶
Whenever a menu needs Page
data, the menu’s get_base_page_queryset()
method is called to get a ‘base’ queryset, which then has additional filter()
and exclude()
statements added to it as required.
By default, get_base_page_queryset()
applies a few simple filters to prevent certain pages appearing in your menus:
Page.objects.filter(live=True, expired=False, show_in_menus=True)
However, if you’d like to filter this result down further, you can do so using something like the following:
Note
The below example shows only a subset of the arguments that are passed to methods using this hook. For a full list of the arguments supplied, see the Argument reference below.
from wagtail.wagtailcore import hooks
@hooks.register('menus_modify_base_page_queryset')
def make_some_changes(
queryset, request, menu_instance, original_menu_tag, current_site,
**kwargs
):
"""
Ensure only pages 'owned' by the currently logged in user are included.
NOTE: MUST ALWAYS RETURN A QUERYSET
"""
if not request.user.is_authenticated():
return queryset.none()
return queryset.filter(owner=self.request.user)
This would ensure that only pages ‘owned’ by currently logged-in user will appear in menus. And the changes will be applied to ALL types of menu, regardless of what template tag is being called to do the rendering.
Or, if you only wanted to change the queryset for a menu of a specific type, you could modify the code slightly like so:
from wagtail.wagtailcore import hooks
@hooks.register('menus_modify_base_page_queryset')
def make_some_changes(
queryset, request, menu_instance, original_menu_tag, current_site,
**kwargs
):
"""
Ensure only pages 'owned' by the currently logged in user are included,
but only for 'main' or 'flat' menus.
NOTE: MUST ALWAYS RETURN A QUERYSET
"""
if menu_type in ('main_menu', 'flat_menu'):
if not request.user.is_authenticated():
return queryset.none()
queryset = queryset.filter(owner=self.request.user)
return queryset # always return a queryset
menus_modify_base_menuitem_queryset¶
When rendering a main or flat menu, top-level items are defined in the CMS, so the menu must fetch that data first, before it can work out whatever additional data is required for rendering.
By default, get_base_menuitem_queryset()
simply returns all of the menu items that were defined in the CMS. Any page data is then fetched separately (using get_base_page_queryset()
), and the two results are combined to ensure that only links to appropriate pages are included in the menu being rendered.
However, if you’d only like to include a subset of the CMS-defined menu item, or make any further modifications, you can do so using something like the following:
Note
The below example shows only a subset of the arguments that are passed to methods using this hook. For a full list of the arguments supplied, see the Argument reference below.
from wagtail.wagtailcore import hooks
@hooks.register('menus_modify_base_menuitem_queryset')
def make_some_changes(
queryset, request, menu_instance, original_menu_tag, current_site,
**kwargs
):
"""
If the request is from a specific site, and the current user is
authenticated, don't show links to some custom URLs.
NOTE: MUST ALWAYS RETURN A QUERYSET
"""
if(
current_site.hostname.startswith('intranet.') and
request.user.is_authenticated()
):
queryset = queryset.exclude(handle__contains="visiting-only")
return queryset # always return a queryset
These changes would be applied to all menu types that use menu items to define the top-level (main and flat menus). If you only wanted to change the queryset for a flat menus, or even a specific flat menu, you could modify the code slightly like so:
from wagtail.wagtailcore import hooks
@hooks.register('menus_modify_base_menuitem_queryset')
def make_some_changes(
queryset, request, menu_instance, original_menu_tag, current_site,
**kwargs
):
"""
When generating a flat menu with the 'action-links' handle, and the
request is for a specific site, and the current user is authenticated,
don't show links to some custom URLs.
NOTE: MUST ALWAYS RETURN A QUERYSET
"""
if(
original_menu_tag == 'flat_menu' and
menu_instance.handle == 'action-links' and
current_site.hostname.startswith('intranet.') and
request.user.is_authenticated()
):
queryset = queryset.exclude(handle__contains="visiting-only")
return queryset # always return a queryset
Hooks for modifying menu items¶
While the above tags are concerned with modifying the data used in a menu, the following hooks are called later on in the rendering process, and allow you to modify the list of MenuItem
or Page
objects before they are sent to a template to be rendered.
There are two hooks you can use to modify menu items, which are called at different stages of preparation.
menus_modify_raw_menu_items¶
This hook allows you to modify the list before it is ‘primed’ (a process that sets ‘href’, ‘text’, ‘active_class’ and ‘has_children_in_menu’ attributes on each item), and before being sent to a parent page’s ‘modify_submenu_items()’ method for further modification (see Using MenuPage to manipulating sub-menu items).
Note
The below example shows only a subset of the arguments that are passed to methods using this hook. For a full list of the arguments supplied, see the Argument reference below.
from wagtail.wagtailcore import hooks
@hooks.register('menus_modify_raw_menu_items')
def make_some_changes(
menu_items, request, parent_page, original_menu_tag, menu_instance,
current_level, **kwargs
):
"""
When rendering the first level of a 'section menu', add a copy of the
first page to the end of the list.
NOTE: prime_menu_items() will attempt to add 'href', 'text' and other
attributes to these items before rendering, so ideally, menu items
should all be `MenuItem` or `Page` instances.
"""
if original_menu_tag == 'section_menu' and current_level == 1:
# Try/except in case menu_items is an empty list
try:
menu_items.append(menu_items[0])
except KeyError:
pass
return menu_items # always return a list
The modified list of menu items will then continue to be processed as normal, being passed to the menu’s ‘prime_menu_items()’ method for priming, and then on to the parent page’s ‘modify_submenu_items()’ for further modification.
menus_modify_primed_menu_items¶
This hook allows you to modify the list of items after they have been ‘primed’ and the modified by a parent page’s ‘modify_submenu_items()’ methods (see Using MenuPage to manipulating sub-menu items).
Note
The below example shows only a subset of the arguments that are passed to methods using this hook. For a full list of the arguments supplied, see the Argument reference below.
from wagtail.wagtailcore import hooks
@hooks.register('menus_modify_primed_menu_items')
def make_some_changes(
menu_items, request, parent_page, original_menu_tag, menu_instance,
current_level, **kwargs
):
"""
When rendering the first level of a 'main menu', add an additional
link to the RKH website
NOTE: This result won't undergo any more processing before sending to
a template for rendering, so you may need to set 'href' and
'text' attributes / keys so that those values are picked up by menu
templates.
"""
if original_menu_tag == 'main_menu' and current_level == 1:
# Just adding a simple dict here, as these values are all the
# template needs to render the link
menu_items.append({
'href': 'https://rkh.co.uk',
'text': 'VISIT RKH.CO.UK',
'active_class': 'external',
})
return menu_items # always return a list
Argument reference¶
In the above examples, **kwargs
is used in hook method signatures to make them accepting of other keyword arguments, without having to declare every single argument that should be passed in. Using this approach helps create leaner, tidier code, and also makes it more ‘future-proof’, since the methods will automatically accept any new arguments that may be added by wagtailmenus in future releases.
Below is a full list of the additional arguments that are passed to methods using the above hooks:
request
- The
HttpRequest
instance that the menu is currently being rendered for. parent_context
- The
Context
instance that the menu is being rendered from. parent_page
- If the menu being rendered is showing ‘children’ of a specific page, this will be the
Page
instance who’s children pages are being displayed. The value might also beNone
if no parent page is involved. For example, if rendering the top level items of a main or flat menu. menu_tag
- The name of the tag that was called to render the current part of the menu. If rendering the first level of a menu, this will have the same value as
original_menu_tag
. If not, it will have the value ‘sub_menu’ (unless you’re using custom tags that pass a different ‘tag_name’ value to the menu classe’s ‘render_from_tag’ method) original_menu_tag
- The name of the tag that was called to initiate rendering of the menu that is currently being rendered. For example, if you’re using the
main_menu
tag to render a multi-level main menu, even thoughsub_menu
may be called to render subsequent additional levels, ‘original_menu_tag’ should retain the value'main_menu'
. Should be one of:'main_menu'
,'flat_menu'
,'section_menu'
or'children_menu'
. Comparable to themenu_type
values supplied to other hooks. menu_instance
- The menu instance that is supplying the data required to generate the current menu. This could be an instance of a model class, like
MainMenu
orFlatMenu
, or a standard python class likeChildrenMenu
orSectionMenu
. original_menu_instance
- The menu instance that is supplying the data required to generate the current menu. This could be an instance of a model class, like
MainMenu
orFlatMenu
, or a standard python class likeChildrenMenu
orSectionMenu
. current_level
- An integer value indicating the ‘level’ or ‘depth’ that is currently being rendered in the process of rendering a multi-level menu. This will start at 1 for the first/top-level items of a menu, and increment by 1 for each additional level.
max_levels
- An integer value indicatiing the maxiumum number of levels that should be rendered for the current menu. This will either have been specified by the developer using the
max_levels
argument of a menu tag, or might have been set in the CMS for a specificMainMenu
orFlatMenu
instance. current_site
- A Wagtail
Site
instance, indicating the site that the current request is for (usually also available asrequest.site
) current_page
- A Wagtail
Page
instance, indicating what wagtailmenus beleives to be the page that is currently being viewed / requested by a user. This might beNone
if you’re using additional views in your project to provide functionality at URLs that don’t map to aPage
in Wagtail. current_page_ancestor_ids
- A list of ids of
Page
instances that are an ‘ancestor’ ofcurrent_page
. current_section_root_page
If
current_page
has a value, this will be the top-most ancestor of that page, from just below the site’s root page. For example, if your page tree looked like the following:Home (Set as 'root page' for the site) ├── About us ├── What we do ├── Careers | ├── Vacancy one | └── Vacancy two ├── News & events | ├── News | | ├── Article one | | └── Article two | └── Events └── Contact us
if the current page was ‘Vacancy one’, the section root page would be ‘Careers’. Or, if the current page was ‘Article one’, the section root page would be ‘News & events’.
use_specific
- An integer value indicating the preferred policy for using
PageQuerySet.specific()
andPage.specific
in rendering the current menu. For more information see: ‘Specific’ pages and menus. allow_repeating_parents
- A boolean value indicating the preferred policy for having pages that subclass
MenuPageMixin
add a repeated versions of themselves to it’s children pages (when rendering a sub_menu for that page). For more information see: The MenuPage and MenuPageMixin models. apply_active_classes
- A boolean value indicating the preferred policy for setting
active_class
attributes on menu items for the current menu. use_absolute_page_urls
- A boolean value indicating the preferred policy for using full/absolute page URLs for menu items representing pages (observed by
prime_menu_items()
when setting thehref
attribute on each menu item). In most cases this will beFalse
, as the default behaviour is to use ‘relative’ URLs for pages.
Using custom menu classes and models¶
Overriding the models used for main menus¶
There are a couple of different approaches for overriding the models used for defining / rendering main menus. The best approach for your project depends on which models you need to override.
Replacing the MainMenuItem
model only¶
If you’re happy with the default MainMenu
model, but wish customise the menu item model (e.g. to add images, description fields, or extra fields for translated strings), you can use the WAGTAILMENUS_MAIN_MENU_ITEMS_RELATED_NAME setting to have main menus use a different model, both within Wagtail’s CMS, and for generating the list of menu_items
used by menu templates.
Within your project, define your custom model by subclassing
AbstractMainMenuItem
:# appname/models.py from django.db import models from django.utils.translation import ugettext_lazy as _ from modelcluster.fields import ParentalKey from wagtail.wagtailimages import get_image_model_string from wagtail.wagtailimages.edit_handlers import ImageChooserPanel from wagtail.wagtailadmin.edit_handlers import FieldPanel, PageChooserPanel from wagtailmenus.models import AbstractMainMenuItem class CustomMainMenuItem(AbstractMainMenuItem): """A custom menu item model to be used by ``wagtailmenus.MainMenu``""" menu = ParentalKey( 'wagtailmenus.MainMenu', on_delete=models.CASCADE, related_name="custom_menu_items", # important for step 3! ) image = models.ForeignKey( get_image_model_string(), blank=True, null=True, on_delete=models.SET_NULL, ) hover_description = models.CharField( max_length=250, blank=True ) # Also override the panels attribute, so that the new fields appear # in the admin interface panels = ( PageChooserPanel('link_page'), FieldPanel('link_url'), FieldPanel('url_append'), FieldPanel('link_text'), ImageChooserPanel('image'), FieldPanel('hover_description'), FieldPanel('allow_subnav'), )
Create migtations for the new model by running:
python manage.py makemigrations appname
Apply the new migrations by running:
python manage.py migrate appname
Add a setting to your project to instruct wagtailmenus to use your custom model instead of the default:
# Set this to the 'related_name' attribute used on the ParentalKey field WAGTAILMENUS_MAIN_MENU_ITEMS_RELATED_NAME = "custom_menu_items"
That’s it! The custom models will now be used instead of the default ones.
Note
Although you won’t be able to see them in the CMS any longer, the default models and any data that was in the original database table will remain intact.
Replacing both the MainMenu
and MainMenuItem
models¶
If you also need to override the MainMenu
model, that’s possible too. But, because the MainMenuItem
model is tied to MainMenu
, you’ll also need to create custom menu item model (whether you wish to add fields / change their behaviour, or not).
Within your project, define your custom models by subclassing the
AbstractMainMenu
andAbstractMainMenuItem
model classes:# appname/models.py from django.db import models from django.utils import translation from django.utils.translation import ugettext_lazy as _ from django.utils import timezone from modelcluster.fields import ParentalKey from wagtail.wagtailadmin.edit_handlers import FieldPanel, MultiFieldPanel, PageChooserPanel from wagtailmenus import app_settings from wagtailmenus.models import AbstractMainMenu, AbstractMainMenuItem class LimitedMainMenu(AbstractMainMenu): limit_from = models.TimeField() limit_to = models.TimeField() def get_base_page_queryset(self): """ If the current time is between 'limit_from' and 'limit_to', only surface pages that are owned by the logged in user """ if( self.request.user and self.limit_from < timezone.now() < self.limit_to ): return self.request.user.owned_pages.filter( live=True, expired=False, show_in_menus=True ) return Page.objects.none() # Like pages, panels for menus are split into multiple tabs. # To update the panels in the 'Content' tab, override 'content_panels' # To update the panels in the 'Settings' tab, override 'settings_panels' settings_panels = AbstractMainMenu.setting_panels += ( MultiFieldPanel( heading=_('Time limit settings'), children=( FieldPanel('limit_from'), FieldPanel('limit_to'), ), ), ) class CustomMainMenuItem(AbstractMainMenuItem): """A minimal custom menu item model to be used by `LimitedMainMenu`. No additional fields / method necessary """ menu = ParentalKey( LimitedMainMenu, # we can use the model from above on_delete=models.CASCADE, related_name=app_settings.MAIN_MENU_ITEMS_RELATED_NAME, )
Create migtations for the new models by running:
python manage.py makemigrations appname
Apply the new migrations by running:
python manage.py migrate appname
Add a setting to your project to tell wagtailmenus to use your custom menu model instead of the default one. e.g:
# settings.py WAGTAILMENUS_MAIN_MENU_MODEL = "appname.LimitedMainMenu"
That’s it! The custom models will now be used instead of the default ones.
Note
Although you won’t be able to see them in the CMS any longer, the default models and any data that was in the original database table will remain intact.
Overriding the models used for flat menus¶
There are a couple of different approaches for overriding the models used for defining / rendering flat menus. The best approach for your project depends on which models you need to override.
Replacing the FlatMenuItem
model only¶
If you’re happy with the default FlatMenu
model, but wish customise the menu item models (e.g. to add images, description fields, or extra fields for translated strings), you can use the WAGTAILMENUS_FLAT_MENU_ITEMS_RELATED_NAME setting to have flat menus use a different model, both within Wagtail’s CMS, and for generating the list of menu_items
used by menu templates.
Within your project, define your custom model by subclassing
AbstractFlatMenuItem
:# apname/models.py from django.db import models from django.utils.translation import ugettext_lazy as _ from modelcluster.fields import ParentalKey from wagtail.wagtailimages import get_image_model_string from wagtail.wagtailimages.edit_handlers import ImageChooserPanel from wagtail.wagtailadmin.edit_handlers import FieldPanel, PageChooserPanel from wagtailmenus.models import AbstractFlatMenuItem class CustomFlatMenuItem(AbstractFlatMenuItem): """A custom menu item model to be used by ``wagtailmenus.FlatMenu``""" menu = ParentalKey( 'wagtailmenus.FlatMenu', on_delete=models.CASCADE, related_name="custom_menu_items", # important for step 3! ) image = models.ForeignKey( get_image_model_string(), blank=True, null=True, on_delete=models.SET_NULL, ) hover_description = models.CharField( max_length=250, blank=True ) # Also override the panels attribute, so that the new fields appear # in the admin interface panels = ( PageChooserPanel('link_page'), FieldPanel('link_url'), FieldPanel('url_append'), FieldPanel('link_text'), ImageChooserPanel('image'), FieldPanel('hover_description'), FieldPanel('allow_subnav'), )
Create migtations for the new models by running:
python manage.py makemigrations appname
Apply the new migrations by running:
python manage.py migrate appname
Add a setting to your project to tell wagtailmenus to use your custom model instead of the default one. e.g:
# settings.py # Use the 'related_name' attribute you used on your custom model's ParentalKey field WAGTAILMENUS_FLAT_MENU_ITEMS_RELATED_NAME = "custom_menu_items"
That’s it! The custom models will now be used instead of the default ones.
Note
Although you won’t be able to see them in the CMS any longer, the default models and any data that was in the original database table will remain intact.
Replacing both the FlatMenu
and FlatMenuItem
models¶
If you also need to override the FlatMenu
model, that’s possible too. But, because the FlatMenuItem
model is tied to FlatMenu
, you’ll also need to create custom menu item model (whether you wish to add fields or their behaviour or not).
Within your project, define your custom models by subclassing the
AbstractFlatMenu
andAbstractFlatMenuItem
model classes:# appname/models.py from django.db import models from django.utils import translation from django.utils.translation import ugettext_lazy as _ from modelcluster.fields import ParentalKey from wagtail.wagtailadmin.edit_handlers import FieldPanel, MultiFieldPanel, PageChooserPanel from wagtailmenus import app_settings from wagtailmenus.panels import FlatMenuItemsInlinePanel from wagtailmenus.models import AbstractFlatMenu, AbstractFlatMenuItem class TranslatedField(object): """ A class that can be used on models to return a 'field' in the desired language, where there a multiple versions of a field to cater for multiple languages (in this case, English, German & French) """ def __init__(self, en_field, de_field, fr_field): self.en_field = en_field self.de_field = de_field self.fr_field = fr_field def __get__(self, instance, owner): active_language = translation.get_language() if active_language == 'de': return getattr(instance, self.de_field) if active_language == 'fr': return getattr(instance, self.fr_field) return getattr(instance, self.en_field) class TranslatedFlatMenu(AbstractFlatMenu): heading_de = models.CharField( verbose_name=_("heading (german)"), max_length=255, blank=True, ) heading_fr = models.CharField( verbose_name=_("heading (french)"), max_length=255, blank=True, ) translated_heading = TranslatedField('heading', 'heading_de', 'heading_fr') # Like pages, panels for menus are split into multiple tabs. # To update the panels in the 'Content' tab, override 'content_panels' # To update the panels in the 'Settings' tab, override 'settings_panels' content_panels = ( MultiFieldPanel( heading=_("Settings"), children=( FieldPanel("title"), FieldPanel("site"), FieldPanel("handle"), ) ), MultiFieldPanel( heading=_("Heading"), children=( FieldPanel("heading"), FieldPanel("heading_de"), FieldPanel("heading_fr"), ), classname='collapsible' ), FlatMenuItemsInlinePanel(), ) class TranslatedFlatMenuItem(AbstractFlatMenuItem): """A custom menu item model to be used by ``TranslatedFlatMenu``""" menu = ParentalKey( TranslatedFlatMenu, # we can use the model from above on_delete=models.CASCADE, related_name=app_settings.FLAT_MENU_ITEMS_RELATED_NAME, ) link_text_de = models.CharField( verbose_name=_("link text (german)"), max_length=255, blank=True, ) link_text_fr = models.CharField( verbose_name=_("link text (french)"), max_length=255, blank=True, ) translated_link_text = TranslatedField('link_text', 'link_text_de', 'link_text_fr') @property def menu_text(self): """Use `translated_link_text` instead of just `link_text`""" return self.translated_link_text or getattr( self.link_page, app_settings.PAGE_FIELD_FOR_MENU_ITEM_TEXT, self.link_page.title ) # Also override the panels attribute, so that the new fields appear # in the admin interface panels = ( PageChooserPanel("link_page"), FieldPanel("link_url"), FieldPanel("url_append"), FieldPanel("link_text"), FieldPanel("link_text_de"), FieldPanel("link_text_fr"), FieldPanel("handle"), FieldPanel("allow_subnav"), )
Create migtations for the new models by running:
python manage.py makemigrations appname
Apply the new migrations by running:
python manage.py migrate appname
Add a setting to your project to tell wagtailmenus to use your custom menu model instead of the default one. e.g:
# settings.py WAGTAILMENUS_FLAT_MENU_MODEL = "appname.TranslatedFlatMenu"
That’s it! The custom models will now be used instead of the default ones.
Note
Although you won’t be able to see them in the CMS any longer, the default models and any data that was in the original database table will remain intact.
Overriding the menu class used by {% section_menu %}
¶
Like the main_menu
and flat_menu
tags, the section_menu
tag uses a Menu
class to fetch all of the data needed to render a menu. Though, because section menus are driven entirely by your existing page tree (and don’t need to store any additional data), it’s just a plain old Python class and not a Django model.
The class wagtailmenus.models.menus.SectionMenu
is used by default, but you can use the WAGTAILMENUS_SECTION_MENU_CLASS_PATH
setting in your project to make wagtailmenus use an alternative class (for example, if you want to modify the base queryset that determines which pages should be included when rendering). To implement a custom classes, it’s recommended that you subclass the SectionMenu
and override any methods as required, like in the following example:
# mysite/appname/models.py
from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailcore.models import Page
from wagtailmenus.models import SectionMenu
class CustomSectionMenu(SectionMenu):
def get_base_page_queryset(self):
# Show draft and expired pages in menu for superusers
if self.request.user.is_superuser:
return Page.objects.filter(show_in_menus=True)
# Resort to default behaviour for everybody else
return super(CustomSectionMenu, self).get_base_page_queryset()
# settings.py
WAGTAILMENUS_SECTION_MENU_CLASS_PATH = "mysite.appname.models.CustomSectionMenu"
Overriding the menu class used by {% children_menu %}
¶
Like all of the other tags, the children_menu
tag uses a Menu
class to fetch all of the data needed to render a menu. Though, because children menus are driven entirely by your existing page tree (and do not need to store any additional data), it’s just a plain old Python class and not a Django model.
The class wagtailmenus.models.menus.ChildrenMenu
is used by default, but you can use the WAGTAILMENUS_CHILDREN_MENU_CLASS_PATH
setting in your project to make wagtailmenus use an alternative class (for example, if you want to modify which pages are included). For custom classes, it’s recommended that you subclass ChildrenMenu
and override any methods as required e.g:
# appname/menus.py
from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailcore.models import Page
from wagtailmenus.models import ChildrenMenu
class CustomChildrenMenu(ChildrenMenu):
def get_base_page_queryset(self):
# Show draft and expired pages in menu for superusers
if self.request.user.is_superuser:
return Page.objects.filter(show_in_menus=True)
# Resort to default behaviour for everybody else
return super(CustomChildrenMenu, self).get_base_page_queryset()
# settings.py
WAGTAILMENUS_CHILDREN_MENU_CLASS_PATH = "mysite.appname.models.CustomChildrenMenu"
Settings reference¶
You can override some of wagtailmenus’ default behaviour by adding one of more of the following to your project’s settings.
Admin / UI settings¶
WAGTAILMENUS_ADD_EDITOR_OVERRIDE_STYLES
¶
Default value: True
By default, wagtailmenus adds some additional styles to improve the readability of the forms on the menu management pages in the Wagtail admin area. If for some reason you don’t want to override any styles, you can disable this behaviour by setting to False
.
WAGTAILMENUS_FLATMENU_MENU_ICON
¶
Default value: ‘list-ol’
Use this to change the icon used to represent ‘Flat menus’ in the Wagtail CMS.
WAGTAILMENUS_FLAT_MENUS_HANDLE_CHOICES
¶
Default value: None
Can be set to a tuple of choices in the standard Django choices format to change the presentation of the FlatMenu.handle
field from a text field, to a select field with fixed choices, when adding, editing or copying a flat menus in Wagtail’s CMS.
For example, if your project uses an ‘info’ menu in the header, a ‘footer’ menu in the footer, and a ‘help’ menu in the sidebar, you could do the following:
WAGTAILMENUS_FLAT_MENUS_HANDLE_CHOICES = (
('info', 'Info'),
('help', 'Help'),
('footer', 'Footer'),
)
WAGTAILMENUS_MAINMENU_MENU_ICON
¶
Default value: 'list-ol'
Use this to change the icon used to represent ‘Main menus’ in the Wagtail CMS.
Default templates and template finder settings¶
WAGTAILMENUS_DEFAULT_CHILDREN_MENU_TEMPLATE
¶
Default value: 'menus/children_menu.html'
The name of the template used for rendering by the {% children_menu %}
tag when no other template has been specified using the template
parameter.
WAGTAILMENUS_DEFAULT_FLAT_MENU_TEMPLATE
¶
Default value: 'menus/flat_menu.html'
The name of the template used for rendering by the {% flat_menu %}
tag when no other template has been specified using the template
parameter.
WAGTAILMENUS_DEFAULT_MAIN_MENU_TEMPLATE
¶
Default value: 'menus/main_menu.html'
The name of the template used for rendering by the {% main_menu %}
tag when no other template has been specified using the template
parameter.
WAGTAILMENUS_DEFAULT_SECTION_MENU_TEMPLATE
¶
Default value: 'menus/section_menu.html'
The name of the template used for rendering by the {% section_menu %}
tag when no other template has been specified using the template
parameter.
WAGTAILMENUS_DEFAULT_SUB_MENU_TEMPLATE
¶
Default value: 'menus/sub_menu.html'
The name of the template used for rendering by the {% sub_menu %}
tag when no other template has been specified using the template
parameter or using the sub_menu_template
parameter on the original menu tag.
WAGTAILMENUS_SITE_SPECIFIC_TEMPLATE_DIRS
¶
Default value: False
If you have a multi-site project where each site may require it’s own set of menu templates, you can change this setting to True
to have wagtailmenus automatically look in additional site-specific locations when finding templates for rendering.
Default tag behaviour settings¶
WAGTAILMENUS_FLAT_MENUS_FALL_BACK_TO_DEFAULT_SITE_MENUS
¶
Default value: False
The default value used for fall_back_to_default_site_menus
option of the {% flat_menu %}
tag when a parameter value isn’t provided.
WAGTAILMENUS_GUESS_TREE_POSITION_FROM_PATH
¶
Default value: True
When not using wagtail’s routing/serving mechanism to serve page objects, wagtailmenus can use the request path to attempt to identify a ‘current’ page, ‘section root’ page, allowing {% section_menu %}
and active item highlighting to work. If this functionality is not required for your project, you can disable it by setting this value to False
.
WAGTAILMENUS_DEFAULT_CHILDREN_MENU_MAX_LEVELS
¶
Default value: 1
The maximum number of levels rendered by the {% children_menu %}
tag when no value has been specified using the max_levels
parameter.
WAGTAILMENUS_DEFAULT_SECTION_MENU_MAX_LEVELS
¶
Default value: 2
The maximum number of levels rendered by the {% section_menu %}
tag when no value has been specified using the max_levels
parameter.
WAGTAILMENUS_DEFAULT_CHILDREN_MENU_USE_SPECIFIC
¶
Default value: 1
(Auto)
Controls how ‘specific’ pages objects are fetched and used during rendering of the {% children_menu %}
tag when no use_specific
value isn’t supplied.
If you’d like to use custom page fields in your children menus (e.g. translated field values or image fields) or if your page models override get_url_parts()
, relative_url()
or other Page
methods involved in URL generation, you’ll likely want to update this.
To find out more about what values are supported and the effect they have, see: ‘Specific’ pages and menus
WAGTAILMENUS_DEFAULT_SECTION_MENU_USE_SPECIFIC
¶
Default value: 1
(Auto)
Controls how ‘specific’ pages objects are fetched and used during rendering of the {% section_menu %}
tag when no alternative value has been specified using the use_specific
parameter.
If you’d like to use custom page fields in your section menus (e.g. translated field values, images, or other fields / methods) or if your page models override get_url_parts()
, relative_url()
or other Page
methods involved in URL generation, you’ll likely want to update this.
To find out more about what values are supported and the effect they have, see: ‘Specific’ pages and menus
Menu class and model override settings¶
WAGTAILMENUS_CHILDREN_MENU_CLASS_PATH
¶
Default value: 'wagtailmenus.models.menus.ChildrenMenu'
Use this to specify a custom menu class to be used by wagtailmenus’ children_menu
tag. The value should be the import path of your custom class as a string, e.g. 'mysite.appname.models.CustomClass'
.
For more details see: Overriding the menu class used by {% children_menu %}
WAGTAILMENUS_FLAT_MENU_MODEL
¶
Default value: 'wagtailmenus.FlatMenu'
Use this to specify a custom model to use for flat menus instead of the default. The model should be a subclass of wagtailmenus.AbstractFlatMenu
.
For more details see: Overriding the models used for flat menus
WAGTAILMENUS_FLAT_MENU_ITEMS_RELATED_NAME
¶
Default value: 'menu_items'
Use this to specify the ‘related name’ that should be used to access menu items from flat menu instances. Used to replace the default FlatMenuItem model with a custom one.
For more details see: Overriding the models used for flat menus
WAGTAILMENUS_MAIN_MENU_MODEL
¶
Default value: 'wagtailmenus.MainMenu'
Use this to specify an alternative model to use for main menus. The model should be a subclass of wagtailmenus.AbstractMainMenu
.
For more details see: Overriding the models used for main menus
WAGTAILMENUS_MAIN_MENU_ITEMS_RELATED_NAME
¶
Default value: 'menu_items'
Use this to specify the ‘related name’ that should be used to access menu items from main menu instances. Used to replace the default MainMenuItem
model with a custom one.
For more details see: Overriding the models used for main menus
WAGTAILMENUS_SECTION_MENU_CLASS_PATH
¶
Default value: 'wagtailmenus.models.menus.SectionMenu'
Use this to specify a custom class to be used by wagtailmenus’ section_menu
tag. The value should be the import path of your custom class as a string, e.g. 'mysite.appname.models.CustomClass'
.
For more details see: Overriding the menu class used by {% section_menu %}
Miscellaneous settings¶
WAGTAILMENUS_ACTIVE_CLASS
¶
Default value: 'active'
The class added to menu items for the currently active page (when using a menu template with apply_active_classes=True
)
WAGTAILMENUS_ACTIVE_ANCESTOR_CLASS
¶
Default value: 'ancestor'
The class added to any menu items for pages that are ancestors of the currently active page (when using a menu template with apply_active_classes=True
)
WAGTAILMENUS_PAGE_FIELD_FOR_MENU_ITEM_TEXT
¶
Default value: 'title'
When preparing menu items for rendering, wagtailmenus looks for a field, attribute or property method on each page with this name to set a text
attribute value, which is used in menu templates as the label for each item. The title
field is used by default.
Note
wagtailmenus will only be able to access custom page fields or methods if ‘specific’ pages are being used (See ‘Specific’ pages and menus). If no attribute can be found matching the specified name, wagtailmenus will silently fall back to using the page’s title
field value.
WAGTAILMENUS_SECTION_ROOT_DEPTH
¶
Default value: 3
Use this to specify the ‘depth’ value of a project’s ‘section root’ pages. For most Wagtail projects, this should be 3
(Root page depth = 1
, Home page depth = 2
), but it may well differ, depending on the needs of the project.
Contributing to wagtailmenus¶
Hey! First of all, thanks for considering to help out!
We welcome all support, whether on bug reports, code, reviews, tests, documentation, translations or just feature requests.
Using the issue tracker¶
The issue tracker is the preferred channel for bug reports, features requests and submitting pull requests. Please don’t use the issue tracker for support requests. If you need help with something that isn’t a bug, you can join our Wagtailmenus support group and ask your question there.
Submitting translations¶
Please submit any new or improved translations through Transifex.
Contributing code changes via pull requests¶
If there are any open issues you think you can help with, please comment on the issue and state your intent to help. Or, if you have an idea for a feature you’d like to work on, raise it as an issue. Once a core contributor has responded and is happy for you to proceed with a solution, you should create your own fork of wagtailmenus, make the changes there. Before committing any changes, we highly recommend that you create a new branch, and keep all related changes within that same branch. When you’ve finished making your changes, and the tests are passing, you can then submit a pull request for review.
What your pull request should include¶
In order to be accepted/merged, your pull request will need to meet the following criteria:
- Documentation updates to cover any new features or changes.
- If you’re not in the list already, add a new line to
CONTRIBUTORS.md
(under the ‘Contributors’ heading) with your name, company name, and an optional twitter handle / email address. - For all new features, please add additional unit tests to
wagtailmenus.tests
, to test what you’ve written. Although the quality of unit tests is the most important thing (they should be readable, and test the correct thing / combination of things), code coverage is important too, so please ensure as many lines of your code as possible are accessed when the unit tests are run.
Developing locally¶
If you’d like a runnable Django project to help with development of wagtailmenus, follow these steps to get started (Mac only). The development environment has django-debug-toolbar and some other helpful packages installed to help you debug with your code as you develop:
In a Terminal window, cd to the wagtailmenus root directory, and run:
pip install -e '.[testing,docs]' -U pip install -r requirements/development.txt
Create a copy of the development settings:
cp wagtailmenus/settings/development.py.example wagtailmenus/settings/development.py
Create a copy of the development urls:
cp wagtailmenus/development/urls.py.example wagtailmenus/development/urls.py
Create
manage.py
by copying the example provided:cp manage.py.example manage.py
To load some test data into the database, run:
python manage.py loaddata wagtailmenus/tests/fixtures/test.json
Create a new superuser so that you can log into the CMS:
python manage.py createsuperuser
Run the project using the standard Django command:
python manage.py runserver
Your local copies of settings/development.py
and manage.py
should be ignored by git when you push any changes, as will anything you add to the wagtailmenus/development/
directory.
Testing locally¶
It’s important that any new code is tested before submitting. To quickly test code in your active development environment, you should first install all of the requirements by running:
pip install -e '.[testing,docs]' -U
Then, run the following command to execute tests:
python runtests.py
Or if you want to measure test coverage, run:
coverage --source=wagtailmenus runtests.py
coverage report
Testing in a single environment is a quick and easy way to identify obvious issues with your code. However, it’s important to test changes in other environments too, before they are submitted. In order to help with this, wagtailmenus is configured to use tox
for multi-environment tests. They take longer to complete, but running them is as simple as running:
tox
Other topics¶
Release packaging guidelines¶
Preparing for a new release¶
Follow the steps outlined below to prep changes in your fork:
Merge any changes from
upstream/master
into your fork’smaster
branch.git fetch upstream git checkout master git merge upstream/master
From your fork’s
master
branch, create a new branch for prepping the release, e.g.:git checkout -b release-prep/2.X.X
Update
__version__
inwagtailmenus/__init__.py
to reflect the new release version.Make sure
CHANGELOG.md
is updated with details of any changes since the last release.Make sure the release notes for the new version have been created / updated in
docs/source/releases/
and are referenced indocs/source/releases/index.rst
.If releasing a ‘final’ version, following an ‘alpha’ or ‘beta’ release, ensure the
a
orb
is removed from the file name for the release, and the reference to it indocs/source/releases/index.rst
.Check that the docs build okay, and fix any errors raised by sphinx:
make html
Commit changes so far:
git commit -am 'Bumped version and updated release notes'
Update the source translation files by running the following from the project’s root directory:
# Update source files django-admin.py makemessages -l en # Commit the changes git commit -am 'Update source translation files'
Push all oustanding changes to github:
git push
Submit your changes as a PR to the main repo via https://github.com/rkhleics/wagtailmenus/compare
Packaging and pushing to PyPi¶
When satisfied with the PR for prepping the files:
From https://github.com/rkhleics/wagtailmenus/pulls, merge the PR into the
master
branch using the “merge commit” option.Locally, cd to the project’s root directory, checkout the
master
branch, and ensure the local copy is up-to-date:workon wagtailmenus cd ../path-to-original-repo git checkout master git pull
Ensure dependencies are up-to-date by running:
pip install -e '.[testing,docs]' -U
Push any updated translation source files to Transifex:
tx push -s -l en
Pull down updated translations from Transifex:
tx pull --a rm -r wagtailmenus/locale/en_GB/ git add *.po
For each language, convert .po files to .mo by running:
msgfmt --check-format -o wagtailmenus/locale/lang/LC_MESSAGES/django.mo wagtailmenus/locale/lang/LC_MESSAGES/django.po
Commit and push all changes so far:
git commit -am 'Pulled updated translations from Transifex and converted to .mo' git push
Create a new tag for the new version and push that too:
git tag -a v2.X.X git push --tags
Create a new source distribution and universal wheel for the new version
python setup.py sdist python setup.py bdist_wheel --universal
Install twine (if not already installed) and upload to the new distribution files to the PyPi test environment.
pip install twine twine upload dist/* -r pypitest
Test that the new test distribution installs okay:
mktmpenv pip install -i https://testpypi.python.org/pypi wagtailmenus deactivate
If all okay, push distribution files to the live PyPi:
twine upload dist/* -r pypi
Edit the release notes for the release from https://github.com/rkhleics/wagtailmenus/releases, by copying and pasting the content from
docs/releases/x.x.x.rst
Crack open a beer - you earned it!
Release notes¶
Wagtailmenus 2.7 (alpha) release notes¶
Note
Wagtailmenus 2.7 is in the alpha stage of development. Any changes detailed below are subject to change before the final 2.7 release.
What’s new?¶
- Added support for Wagtail 2.0 and Django 2.0
- Dropped support for Wagtail versions 1.8 to 1.9
- Dropped support for Django versions 1.5 to 1.10
- Dropped support for Python 2 and 3.3
Minor changes & bug fixes¶
Various ‘Python 3 only’ code optimisations:
- Removed __future__ unicode handling imports throughout.
- Replaced ‘old style’ class definitions with ‘new style’ ones.
- Simplified super() syntax throughout to the cleaner Python 3 implementation.
- Fixed deprecation warnings pertaining to use of
TestCase.assertRaisesRegexp
. - Fixed an issue that was preventing translated field label text appearing for the
handle
field when using theFLAT_MENUS_HANDLE_CHOICES
setting (Contributed by @jeromelebleu)
Upgrade considerations¶
- This version only officially supports Wagtail and Django versions from 1.10 to 2.0. If you’re using anything earlier than that, you should consider updating your project. If upgrading Wagtail or Django isn’t an option, it might be best to stick with wagtailmenus 2.6 for now (which is a LTS release).
- This version also drops support for anything earlier than Python 3.4.
Following the standard deprecation period, the following classes, methods and behaviour has been removed:
- The
wagtailmenus.models.menus.MenuFromRootPage
class was removed. - The
__init__()
method ofwagtailmenus.models.menus.ChildrenMenu
no longer accepts a root_page keyword argument. The parent page should be passed using the parent_page keyword instead. - The root_page attribute has been removed from the
wagtailmenus.models.menus.ChildrenMenu
class. Use the parent_page attribute instead. - The
sub_menu
template tag no longer accepts a stop_at_this_level keyword argument. - The
get_sub_menu_items_for_page()
andprime_menu_items()
methods have been removed fromwagtailmenus.templatetags.menu_tags
. - The
get_attrs_from_context()
method has been removed fromwagtailmenus.utils.misc
. - The
get_template_names()
andget_sub_menu_template_names()
methods have been removed fromwagtailmenus.utils.template
and the redundantwagtailmenus.utils.template
module removed.
Wagtailmenus 2.6.0 release notes¶
Note
Wagtailmenus 2.6 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 (for at least 8 months).
Note
Wagtailmenus 2.6 will be the last LTS release to support Python 2 or Python 3.3.
Note
Wagtailmenus 2.6 will be the last LTS release to support Wagtail versions 1.5 to 1.9.
What’s new?¶
Improved compatibility with alternative template backends¶
Wagtailmenus has been updated to use backend-specific templates for rendering, making it compatible with template backends other than Django’s default backend (such as jinja2
).
Although the likelihood of this new behavior introducing breaking changes to projects is minute, it is turned OFF by default for now, in order to give developers time to make any necessary changes. However, by version 2.8 the updated behaviour will replace the old behaviour completely, becoming non optional.
To start using wagtailmenus with an alternative backend now (or to test your project’s compatibility in advance), you can turn the updated behaviour ON by adding the following to your project’s settings:
WAGTAILMENUS_USE_BACKEND_SPECIFIC_TEMPLATES = True
Thank you to Nguyễn Hồng Quân (hongquan) for contributing this!
New tabbed interface for menu editing¶
In an effort to improve the menu editing UI, Wagtail’s TabbedInterface
is now used to split a menu’s fields into two tabs for editig: Content and Settings; with the latter including panels for the max_levels
and use_specific
fields (which were previously tucked away at the bottom of the edit page), and the former for everything else.
Two new attributes, content_panels
and settings_panels
have also been added to AbstractMainMenu
and AbstractFlatMenu
to allow the panels for each tab to be updated independently.
If for any reason you don’t wish to use the tabbed interface for editing custom menu models, the panels
attribute is still supported, and will setting that will result in all fields appearing in a single list (as before). However, the panels
attribute currently present on the AbstractFlatMenu
and AbstractMainMenu
models is now deprecated and will be removed in the future releases (see below for more info).
Built-in compatibility with wagtail-condensedinlinepanel
¶
In an effort to improve the menu editing UI, wagtailmenus now has baked-in compatibility with wagtail-condensedinlinepanel
. As long as a compatible version (at least 0.3) of the app is installed, wagtailmenus will automatically use CondensedInlinePanel
instead of Wagtail’s built-in InlinePanel
for listing menu items, giving menu editors some excellent additional features, including drag-and-drop reordering and the ability to add a new item into any position.
If you have custom Menu models in your project that use the panels
attribute to customise arrangement of fields in the editing UI, you might need to change the your panel list slightly in order to see the improved menu items list after installing. Where you might currently have something like:
class CustomMainMenu(AbstractMainMenu):
...
panels = (
...
InlinePanel('custom_menu_items'),
..
)
class CustomFlatMenu(AbstractFlatMenu):
...
panels = (
...
InlinePanel('custom_menu_items'),
..
)
You should import MainMenuItemsInlinePanel
and FlatMenuItemsInlinePanel
from wagtailmenus.panels
and use them instead like so:
from wagtailmenus.panels import FlatMenuItemsInlinePanel, MainMenuItemsInlinePanel
class CustomMainMenu(AbstractMainMenu):
...
panels = (
...
MainMenuItemsInlinePanel(), # no need to pass any arguments!
..
)
class CustomFlatMenu(AbstractFlatMenu):
...
panels = (
...
FlatMenuItemsInlinePanel(), # no need to pass any arguments!
..
)
Minor changes & bug fixes¶
- Updated tests to test compatibility with Wagtail 1.13.
Deprecations¶
Menu.get_template_engine()
¶
This method is deprecated in favour of using Django’s generic ‘get_template’ and ‘select_template’ methods, which return backend-specific template instances instead of django.template.Template instances.
AbstractMainMenu.panels
and AbstractFlatMenu.panels
¶
If you are referencing AbstractMainMenu.panels
or AbstractFlatMenu.panels
anywhere, you should update your code to reference the content_panels
or settings_panels
attribute instead, depending on which panels you’re trying to make use of.
If you’re overriding the panels
attribute on a custom menu model in order to make additional fields available in the editing UI (or change the default field display order), you might also want to think about updating your code to override the content_panels
and settings_panels
attributes instead, which will result in fields being split between two tabs (Content and Settings). However, this is entirely optional.
Upgrade considerations¶
- Following the standard deprecation period, any modify_submenu_items() methods implemented on custom Page type models must now accept a ‘use_absolute_page_urls’ keyword argument. See the 2.4 release notes for more info: http://wagtailmenus.readthedocs.io/en/stable/releases/2.4.0.html
Wagtailmenus 2.5.2 release notes¶
There are no changes in this release
Wagtailmenus 2.5.1 release notes¶
This is a maintenence release to fix a bug that was resulting in Django creating additional migrations for the app when running the makemigrations
command after changing the project’s default language to something other than “en”.
Thanks to @philippbosch from A Color Bright for submitting the fix.
Wagtailmenus 2.5.0 release notes¶
What’s new?¶
Class-based rendering behaviour for menus¶
This version of wagtailmenus sees quite a large refactor in an attempt to address a large amount of repetition and inconsistency in tempate tag code, and to also break the process of rendering menus down into more clearly defined steps that can be overridden individually where needed.
While the existing template tags remain, and are still the intended method for initiating rendering of menus from templates, their responsibilities have diminished somewhat. They are now only really responsible for performing basic validation on the option values passed to them - everything else is handled by the relevant Menu class.
The base Menu
class defines the default ‘rendering’ logic and establishes a pattern of behaviour for the other classes to follow. Then, the more specific classes simply override the methods they need to produce the same results as before.
Below is an outline of the new process, once the menu tag has prepared it’s option values and is ready to hand things over to the menu class:
- The menu class’s
render_from_tag()
method is called. It takes the current context, as well as any ‘option values’ passed to / prepared by the template tag. render_from_tag()
calls the class’sget_contextual_vals_from_context()
method, which analyses the current context and returns aContextualVals
instance, which will serve as a convenient (read-only) reference for ‘contextual’ data throughout the rest of the process.render_from_tag()
calls the class’sget_option_vals_from_options()
method, which analyses the provided option values and returns anOptionVals
instance, which will serve as a convenient (read-only) reference for ‘option’ data throughout the rest of the process. The most common attributes are accessible directly (e.g.opt_vals.max_levels
andopt_vals.template_name
), but some menu-specific options, or any additional values passed to the tag, will be stored as a dictionary, available asopt_vals.extra
.render_from_tag()
calls the class’sget_instance_for_rendering()
method, which takes the preparedContextualVals
andOptionVals
instances, and uses them to get or create and return a relevant instance to use for rendering.- In order for the menu instance to handle the rest of the rendering process, it needs to be able to access the
ContextualVals
andOptionVals
instances that have already been prepared, so those values are passed to the instance’sprepare_to_render()
method, where references to them are saved on the instance as private attributes;self._contextual_vals
and self._option_vals. - With access to everything it needs, the instance’s
render_to_template()
method is called. This in turn calls two more instance methods. - The
get_context_data()
method creates and returns a dictionary of values that need to be available in the template. This includes obvious things such as a list ofmenu_items
for the current level, and other not-so-obvious things, which are intended to be picked up by thesub_menu
tag (if it’s being used in the template to render additional levels). The menu items are provided by theget_menu_items_for_rendering()
method, which in turn splits responsibility for sourcing, priming, and modifying menu items between three other methods:get_raw_menu_items()
,prime_menu_items()
andmodify_menu_items()
, respectively. - The
get_template()
method indentifies and returns an appropriateTemplate
instance that can be used for rendering. - With the data and template gathered,
render_to_template()
then converts the data into aContext
object and sends it to the template’srender()
method, creating a string representation of the menu, which is sent back for inclusion in the original template.
Hooks added to give developers more options for manipulating menus¶
While wagtailmenus has long supported the use of custom classes for most things (allowing developers to override methods as they see fit), for a long time, I’ve felt that it should be easier to override some core/shared behaviour without the technical overhead of having to create and maintain multiple custom models and classes. So, wagtailmenus now supports several ‘hooks’, which allow you to do just that.
They use the hooks mechanism from Wagtail, so you may already be familiar with the concept. For more information and examples, see the new section of the documentation: Using hooks to modify menus.
New ‘autopopulate_main_menus’ command added¶
The ‘autopopulate_main_menus’ command has been introduced to help developers integrate wagtailmenus into an existing project, by removing some of the effort that is often needed to populating main menu for each project from scratch. It’s been introduced as an extra (optional) step to the instruction in: Installing wagtailmenus.
Utilises the new add_menu_items_for_pages()
method, mentioned below.
New ‘add_menu_items_for_pages()’ method added for main & flat menus¶
For each page in the provided PageQuerySet
a menu item will be added to the menu, linking to that page. The method has was added to the MenuWithMenuItems
model class, which is subclassed by AbstractMainMenu
and AbstractFlatMenu
, so you should be able to use it on custom menu model objects, as well as objects using the default models.
Overriding ‘get_base_page_queryset()’ now effects top-level menu items too¶
Previously, if you overrode get_base_page_queryset() on a custom main menu or flat menu model, the page-tree driven part of the menu (anything below the top-level) would respect that, but top-level menu items linking to pages excluded by get_base_page_queryset() would still be rendered.
Now, ‘top_level_items’ has been refactored to call get_base_page_queryset() to filter down and return page data for items at the top level too, so developers can always expect changes to get_base_page_queryset() to be reflected throughout entire menus.
‘MenuItemManager.for_display()’ now returns all items, regardless of the status of linked pages¶
When sourcing data for a main or flat menu, it doesn’t make sense to apply two sets of filters relating to pages status/visibility, so ‘for_display’ now simply returns ALL menu items defined for a menu, and any unsuitable page links are filtered out in a menu instances ‘top_level_items’ by calling upon ‘get_base_page_queryset’.
Minor changes & bug fixes¶
- Fixed an issue with runtests.py that was causing tox builds in Travis CI to report as successful, even when tests were failing. Contributed by Oliver Bestwalter (obestwalter).
- The
stop_at_this_level
argument for thesub_menu
tag has been officially deprecated and the feature removed from documentation. It hasn’t worked for a few versions and nobody has mentioned it, so this is the first step to removing it completely. - Made the logic in ‘pages_for_display’ easier to override on custom menu
classes by breaking it out into a separate ‘get_pages_for_display()’
method (that isn’t decorated with
cached_property
). - Added support for Wagtail 1.12
Upgrade considerations¶
The ChildrenMenu’s ‘root_page’ attribute is deprectated in favour of ‘parent_page’¶
In previous versions, the ChildrenMenu and SectionMenu classes both extended the same MenuFromRootPage
class, which takes root_page
as an init argument, then stores a reference to that page using an attribute of the same name.
The ChildrenMenu class has now been updated to use parent_page
as an init argument and attribute name instead, which feels like a much better fit. This same terminology has also been adopted for the SubMenu class too.
If you’re subclassing the ChildrenMenu class in your project, please update any code referencing root_page to use parent_page instead. Support for the old name will be removed in version 2.7.
‘MenuWithMenuItems.get_base_menuitem_queryset()’ no longer filters the queryset¶
By default, the queryset returned by ‘get_base_menuitem_queryset’ on menu instances will now return ALL menu items defined for that menu, regardless of the status / visibility of any linked pages.
Previously, the result was filtered to only include pages with ‘live’ status, and with a True ‘show_in_menus’ value.
If you’re calling ‘get_base_menuitem_queryset’ anywhere in your project, and are relying on the original method to return the same value as it did before, you will need to apply the additional filters to the queryset, like so:
from django.db.models import Q
...
menu_item_qs = menu.get_base_menuitem_queryset()
menu_item_qs = menu_item_qs.filter(
Q(link_page__isnull=True) |
Q(link_page__live=True) &
Q(link_page__expired=False) &
Q(link_page__show_in_menus=True)
)
‘MenuItemManager.for_display()’ no longer filters the queryset¶
If you are subclasssing MenuItemManger
to create managers for your custom menu item models, and are relying on the original ‘for_display’ method to filter out links based on their linked page’s status/visibility, you may wish to revise your code to filter out the pages as before, like so:
from django.db.models import Q
from wagtailmenus.managers import MenuItemManager
...
class CustomMenuItemManager(MenuItemManager):
def for_display(self):
qs = super(CustomMenuItemManager, self).for_display()
qs = qs.filter(
Q(link_page__isnull=True) |
Q(link_page__live=True) &
Q(link_page__expired=False) &
Q(link_page__show_in_menus=True)
)
# Now apply any custom filters
...
# Return queryset
return qs
The sub_menu
tag will raise an error if used in a non-menu template¶
Despite the docs always having stated that the ‘sub_menu’ tag is only intended for use in menu templates for other types of menu; Up until now, it has functioned similarly to the ‘children_menu’ tag if used in a regular Django template. But, if you try to call ‘sub_menu’ from anything other than a menu template now, a SubMenuUsageError
error will now be raised.
I highly doubt this will trip anybody up, but sorry if it does. Recent versions of Django seem to swallow deprecation warnings when they occur in the course of rendering a template tag, so even if there were a deprecation period for this, the warnings probably wouldn’t have been seen by anyone.
wagtailmenus.models.menus.MenuFromRootPage
is deprecated¶
With ChildrenMenu
being refactored to use ‘parent_page’ as an attribute instead of ‘root_page’, and the new SubMenu
menu class taking a similar approach, the MenuFromRootPage
name only seems relevent to SectionMenu
, so it has been deprecated in favour of using a more generically-named MenuFromPage
class, which is subclassed by all three.
wagtailmenus.menu_tags.prime_menu_items()
is deprecated¶
The method has been superseded by new logic added to the Menu
class.
wagtailmenus.menu_tags.get_sub_menu_items_for_page()
is deprecated¶
The method has been superseded by new logic added to the Menu
class.
wagtailmenus.utils.misc.get_attrs_from_context()
is deprecated¶
The method has been superseded by new logic added to the Menu
class.
wagtailmenus.utils.template.get_template_names()
is deprecated¶
The method has been superseded by new logic added to the Menu
class.
wagtailmenus.utils.template.get_sub_menu_template_names()
is deprecated¶
The method has been superseded by new logic added to the Menu
class.
Wagtailmenus 2.4.3 release notes¶
This is a maintenence release to fix a bug that was resulting in Django creating additional migrations for the app when running the makemigrations
command after changing the project’s default language to something other than “en”.
Thanks to @philippbosch from A Color Bright for submitting the fix.
Wagtailmenus 2.4.2 release notes¶
There are no changes in this release.
Wagtailmenus 2.4.1 release notes¶
This is a maintenence release to add a migration that should have been included in the previous release, but wasn’t. Thanks to Stuart George (@stuartaccent) for reporting and submitting the fix.
If you experience problems after upgrading from 2.4.0 to 2.4.1 (due to your project creating it’s own, conflicting migration), please try running pip uninstall wagtailmenus
first, then install the latest version.
Wagtailmenus 2.4.0 release notes¶
What’s new?¶
Check out the new documentation!¶
It’s been a long wait, but I finally got around to making it happen. Wagtailmenus now has easily navigatable and searchable documentation, kindly hosted by readthedocs.org. Find it at http://wagtailmenus.readthedocs.io/
New get_text_for_repeated_menu_item()
method on MenuPageMixin
and MenuPage
models¶
The new method is called by get_repeated_menu_item()
to get a string to use to populate the text
attribute on repeated menu items.
The method is designed to be overriden in cases where the text value needs to come from different fields. e.g. in multilingual site where different translations of ‘repeated_item_text’ must be surfaced.
By default, if the repeated_item_text
field is left blank, the WAGTAILMENUS_PAGE_FIELD_FOR_MENU_ITEM_TEXT
is respected, instead of
just returning Page.title
.
New use_absolute_page_urls
param added to template tags¶
The new parameter allows you to render menus that use ‘absolute’ URLs
for pages (including the protocol/domain derived from the relevant
wagtailcore.models.Site
object), instead of the ‘relative’ URLs used by
default.
Other minor changes¶
- Adjusted Meta classes on menu item models so that common behaviour is defined once in AbastractMenuItem.Meta.
- Refactored the AbstractMenuItem’s menu_text property method to improve code readability, and better handle instances where neither link_text or link_page are set.
Upgrade considerations¶
The signature of the modify_submenu_items()
and get_repeated_menu_item()
methods on MenuPage
and MenuPageMixin
models has been updated to accept a new use_absolute_page_urls
keyword argument.
If you’re overrding either of these methods in your project, you should think about updating the signatures of those methods to accept
the new argument and pass it through when calling super()
, like in the following example:
from wagtailmenus.models import MenuPage
class ContactPage(MenuPage):
...
def modify_submenu_items(
self, menu_items, current_page, current_ancestor_ids,
current_site, allow_repeating_parents, apply_active_classes,
original_menu_tag, menu_instance, request, use_absolute_page_urls,
):
# Apply default modifications first of all
menu_items = super(ContactPage, self).modify_submenu_items(
menu_items, current_page, current_ancestor_ids, current_site, allow_repeating_parents, apply_active_classes, original_menu_tag,
menu_instance, request, use_absolute_page_urls
)
"""
If rendering a 'main_menu', add some additional menu items to the end
of the list that link to various anchored sections on the same page
"""
if original_menu_tag == 'main_menu':
base_url = self.relative_url(current_site)
menu_items.extend((
{
'text': 'Get support',
'href': base_url + '#support',
'active_class': 'support',
},
{
'text': 'Speak to someone',
'href': base_url + '#call',
'active_class': 'call',
},
{
'text': 'Map & directions',
'href': base_url + '#map',
'active_class': 'map',
},
))
return menu_items
def get_repeated_menu_item(
self, current_page, current_site, apply_active_classes,
original_menu_tag, request, use_absolute_page_urls,
):
item = super(ContactPage, self).get_repeated_menu_item(
current_page, current_site, apply_active_classes,
original_menu_tag, request, use_absolute_page_urls,
)
item.text = 'Eat. Sleep. Rave. Repeat!'
return item
If you choose NOT to update your versions of those methods to accept the use_absolute_page_urls
keyword argument, you will continue to see deprecation warnings until version 2.6.0
, when it will be a requirement, and your existing code will no longer work.
You might want to consider adopting a more future-proof approach to overriding the methods from MenuPage
and MenuPageMixin
, so that new keyword arguments added in future will be catered for automatically.
Below shows a version of the above code example, modified to use **kwargs
in methods:
from wagtailmenus.models import MenuPage
class ContactPage(MenuPage):
...
def modify_submenu_items(self, menu_items, **kwargs):
# Apply default modifications first of all
menu_items = super(ContactPage, self).modify_submenu_items(menu_items, **kwargs)
"""
If rendering a 'main_menu', add some additional menu items to the end
of the list that link to various anchored sections on the same page
"""
if kwargs['original_menu_tag'] == 'main_menu':
base_url = self.relative_url(kwargs['current_site'])
menu_items.extend((
{
'text': 'Get support',
'href': base_url + '#support',
'active_class': 'support',
},
{
'text': 'Speak to someone',
'href': base_url + '#call',
'active_class': 'call',
},
{
'text': 'Map & directions',
'href': base_url + '#map',
'active_class': 'map',
},
))
return menu_items
def get_repeated_menu_item(self, current_page, **kwargs):
item = super(ContactPage, self).get_repeated_menu_item(current_page, **kwargs)
item.text = 'Eat. Sleep. Rave. Repeat!'
return item
Wagtailmenus 2.3.2 release notes¶
This is a maintenence release to fix a bug that was resulting in
{% sub_menu %}
being called recursively (until raising a “maximum
recursion depth exceeded” exception) if a ‘repeated menu item’ was added at
anything past the 2nd level. Thanks to @pyMan for raising/investigating.
Wagtailmenus 2.3.1 release notes¶
This is a maintenence release to fix to address the following:
- Code example formatting fixes, and better use of headings in README.md.
- Added ‘on_delete=models.CASCADE’ to all relationship fields on models where no ‘on_delete’ behaviour was previously set (Django 2.0 compatibility).
- Marked a missing string for translation (@einsfr)
- Updated translations for Lithuanian, Portuguese (Brazil), and Russian. Many thanks to @mamorim, @treavis and @einsfr!
Wagtailmenus 2.3.0 release notes¶
- What’s new?
- Introducing the
AbstractLinkPage
model! - Introducing the
MenuPageMixin
model! - MenuPage methods updated to accept
request
keyword argument - All
Menu
classes are now ‘request aware’ - Added
get_base_page_queryset()
method to allMenu
classes - Overridable menu classes for
section_menu
andchildren_menu
tags - Other minor changes
- Introducing the
- Upgrade considerations
What’s new?¶
Introducing the AbstractLinkPage
model!¶
The newly added AbstractLinkPage
model can be easily sub-classed and used
in projects to create ‘link pages’ that act in a similar fashion to menu items
when appearing in menus, but can be placed in any part of the page tree.
Introducing the MenuPageMixin
model!¶
Most of the functionality from MenuPage
model has been abstracted out to a MenuPageMixin
model, that can more easily be mixed in to existing page type models.
MenuPage methods updated to accept request
keyword argument¶
The modify_submenu_items()
, has_submenu_items()
and get_repeated_menu_item()
methods on MenuPageMixin
/ MenuPage
have been updated to accept a new request keyword argument, which is used to pass in the current HttpRequest
that the menu is being rendered for.
All Menu
classes are now ‘request aware’¶
A new set_request()
method on all Menu
classes is used to set a
request
attribute on the Menu
instance, immediately after
initialisation, allowing you to referencing self.request
from most methods
to access the current HttpRequest
object
Added get_base_page_queryset()
method to all Menu
classes¶
That can be overridden to change the base QuerySet
used when identifying
pages to be included in a menu when rendering. For example developers could use self.request.user
to only ever include pages that the current user has some
permissions for.
Overridable menu classes for section_menu
and children_menu
tags¶
Added the WAGTAILMENUS_SECTION_MENU_CLASS_PATH
setting, which can be used
to override the Menu
class used when using the {% section_menu %}
tag.
Added the WAGTAILMENUS_CHILDREN_MENU_CLASS_PATH
setting, which can be used
to override the Menu
class used when using the {% children_menu %}
tag.
Other minor changes¶
- Added wagtail 1.10 and django 1.11 test environments to tox
- Renamed
test_frontend.py
totest_menu_rendering.py
- In situations where
request.site
hasn’t been set by wagtail’sSiteMiddleware
, the wagtailmenus context processor will now use the default site to generate menus with. - Updated
AbstractMenuItem.clean()
to only ever return field-specific validation errors, because Wagtail doesn’t render non-field errors for related models added to the editor interface usingInlinePanel
. - Refactored
runtest.py
to accept adeprecation
argument that can be used to surface deprecation warnings that arise when running tests. - Added Russian translations (submitted by Alex einsfr).
Upgrade considerations¶
Several methods on the MenuPage
model have been updated to accept a
request
parameter. If you’re upgrading to version 2.3.0
from a previous
version, it’s not necessary to make any changes immediately in order for
wagtailmenus to work, but if you’re using the MenuPage
class in your
project, and are overriding any of the following methods:
modify_submenu_items()
has_submenu_items()
get_repeated_menu_item()
Then you should think about updating the signatures of those methods to accept
the new argument and pass it through when calling super()
. See the
following code for an example:
from wagtailmenus.models import MenuPage
class ContactPage(MenuPage):
...
def modify_submenu_items(
self, menu_items, current_page, current_ancestor_ids,
current_site, allow_repeating_parents, apply_active_classes,
original_menu_tag, menu_instance, request
):
# Apply default modifications first of all
menu_items = super(ContactPage, self).modify_submenu_items(
menu_items, current_page, current_ancestor_ids, current_site, allow_repeating_parents, apply_active_classes, original_menu_tag,
menu_instance, request)
"""
If rendering a 'main_menu', add some additional menu items to the end
of the list that link to various anchored sections on the same page
"""
if original_menu_tag == 'main_menu':
base_url = self.relative_url(current_site)
"""
Additional menu items can be objects with the necessary attributes,
or simple dictionaries. `href` is used for the link URL, and `text`
is the text displayed for each link. Below, I've also used
`active_class` to add some additional CSS classes to these items,
so that I can target them with additional CSS
"""
menu_items.extend((
{
'text': 'Get support',
'href': base_url + '#support',
'active_class': 'support',
},
{
'text': 'Speak to someone',
'href': base_url + '#call',
'active_class': 'call',
},
{
'text': 'Map & directions',
'href': base_url + '#map',
'active_class': 'map',
},
))
return menu_items
def has_submenu_items(
self, current_page, allow_repeating_parents, original_menu_tag,
menu_instance, request
):
"""
Because `modify_submenu_items` is being used to add additional menu
items, we need to indicate in menu templates that `ContactPage` objects
do have submenu items in main menus, even if they don't have children
pages.
"""
if original_menu_tag == 'main_menu':
return True
return super(ContactPage, self).has_submenu_items(
current_page, allow_repeating_parents, original_menu_tag,
menu_instance, request)
If you choose NOT to update your versions of those methods to accept the
``request`` keyword argument, you will continue to see deprecation warnings
until version ``2.5.0``, when it will be a requirement, and your existing code
will no longer work.
Wagtailmenus 2.2.3 release notes¶
This is a maintenence release to fix a bug that was resulting in
{% sub_menu %}
being called recursively (until raising a “maximum
recursion depth exceeded” exception) if a ‘repeated menu item’ was added at
anything past the 2nd level. Thanks to @pyMan for raising/investigating.
Wagtailmenus 2.2.2 release notes¶
This is a maintenence release to improve general code quality and make project management easier. There are no compatiblilty issues to worry about.
What’s changed?¶
- Update codebase to better handle situations where
request
isn’t available in the context when rendering, orrequest.site
hasn’t been set. - Got the project set up in Transifex (finally!): https://www.transifex.com/rkhleics/wagtailmenus/
- Updated translatable strings throughout the project to use named variable substitution, and unmarked a few exception messages.
- Added Lithuanian translations (submitted by Matas Dailyda).
Wagtailmenus 2.2.1 release notes¶
This is a maintenence release to improve code quality and project management.
What’s changed?¶
- Updated travis/tox test settings to test against Wagtail 1.9 & Django 1.10.
- Removed a couple of less useful travis/tox environment tests to help with test speed.
- Made use of ‘extras_require’ in setup.py to replace multiple requirements files.
- Optimised the app_settings module so that we can ditch the questionable stuff we’re doing with global value manipulation on app load (solution inspired by django-allauth).
- Added new semantic version solution to the project (inspired by wagtail).
Wagtailmenus 2.2.0 release notes¶
What’s new?¶
Wagtailmenus now makes use of django’s built-in
django.template.loader.select_template()
method to provide a more intuitive way for developers to override templates for specific menus without having to explicitly specify alternative templates via settings or via thetemplate
andsub_menu_template
options for each menu tag.See the updated documentation for each tag for information about where wagtailmenus looks for templates.
Added the
WAGTAILMENUS_SITE_SPECIFIC_TEMPLATE_DIRS
setting to allow developers to choose to have wagtailmenus look in additional site-specific locations for templates to render menus.wagtailmenus no longer errors if a page’s
relative_url()
method raises aTypeError
.Brazilian Portuguese language translations added by @MaxKurama.
Wagtailmenus 2.1.4 release notes¶
This is a maintenence release to fix a bug that was resulting in
{% sub_menu %}
being called recursively (until raising a “maximum
recursion depth exceeded” exception) if a ‘repeated menu item’ was added at
anything past the 2nd level. Thanks to @pyMan for raising/investigating.
Wagtailmenus 2.1.3 release notes¶
This is a maintenence release to fix a bug in the section_menu tag
that
arose when attempting to apply the correct active class to section_root
when the modify_submenu_items()
method has been overridden to return additional items without an active_class
attribute (like in the example code in README)
Wagtailmenus 2.1.2 release notes¶
This is a maintenence release to fix a bug that was preventing reordered menu
items from retaining their new order after saving (the Meta
class on the
new abstract models had knocked out the sort_order
ordering from wagtail.wagtailcore.models.Orderable
).
Wagtailmenus 2.1.1 release notes¶
This is a maintenence release to fix a bug introduced in 2.1.0 preventing the app from being installed via pip
Wagtailmenus 2.1.0 release notes¶
There is a know bug with this release when attempting to install using pip. Please update to v2.1.1 instead.
What’s new?¶
- Added official support for wagtail v1.8
- Added
WAGTAILMENUS_MAIN_MENU_MODEL
andWAGTAILMENUS_FLAT_MENU_MODEL
settings to allow the default main and flat menu models to be swapped out for custom models. - Added
WAGTAILMENUS_MAIN_MENU_ITEMS_RELATED_NAME
andWAGTAILMENUS_FLAT_MENU_ITEMS_RELATED_NAME
settings to allow the default menu item models to be swapped out for custom models. - Added the
WAGTAILMENUS_PAGE_FIELD_FOR_MENU_ITEM_TEXT
setting to allow developers to specify a page attribute other than title to be used to populate thetext
attribute for menu items linking to pages. - Added german translations by Pierre (@bloodywing).
Wagtailmenus 2.0.3 release notes¶
This is a maintenence release to fix a migration related issue raised by @urlsangel: https://github.com/rkhleics/wagtailmenus/issues/85
Wagtailmenus 2.0.2 release notes¶
This release is broken and shouldn’t be use. Skip straight to v2.0.3 instead.
Wagtailmenus 2.0.1 release notes¶
This is a maintenence release to fix a bug reported by some users when using the {% main_menu %}
tag without a max_levels
value.
Wagtailmenus 2.0.0 release notes¶
What’s new?¶
New use_specific
and max_levels
fields for menu models¶
MainMenu and FlatMenu models now have two new fields:
use_specific
: To allow the defaultuse_specific
setting when rendering that menu to be changed via the Wagtail CMS.max_levels
: To allow the default max_levels setting when rendering that menu to be changed via the admin area.
Find the field in the collapsed ADVANCED SETTINGS panel at the bottom of the edit form
More use_specific
options available¶
The use_specific
menu tag argument can now be one of 4 integer values,
allowing for more fine-grained control over the use of Page.specific
and PageQuerySet.specific()
when rendering menu tags.
Developers not using the MenuPage
model or overriding any of wagtail’s
Page` methods involved in URL generation can now enjoy better performance by
choosing not to fetch any specific pages at all during rendering. Simply pass ``use_specific=USE_SPECIFIC_OFF
or use_specific=0
to the tag, or update the use_specific
field value on your MainMenu
or FlatMenu
objects via
the Wagtail admin area.
Basic argument valdation added to template tags¶
The max_levels
, use_specific
, parent_page
and menuitem_or_page
arguments passed to all template tags are now checked to ensure their values
are valid, and if not, raise a ValueError
with a helpful message to aid
debugging.
Upgrade considerations¶
Dropped features¶
- Dropped support for the
WAGTAILMENUS_DEFAULT_MAIN_MENU_MAX_LEVELS
andWAGTAILMENUS_DEFAULT_FLAT_MENU_MAX_LEVELS
settings. Default values are now set using themax_levels
field on the menu objects themselves. - Dropped support for the
WAGTAILMENUS_DEFAULT_MAIN_MENU_USE_SPECIFIC
andWAGTAILMENUS_DEFAULT_FLAT_MENU_USE_SPECFIC
settings. Default values are now set using theuse_specific
field on the menu objects themselves. - The
has_submenu_items()
method onMenuPage
no longer accepts the check_for_children argument. - The
modify_submenu_items()
andhas_submenu_items()
methods on theMenuPage
model now both accept an optionalmenu_instance
kwarg. - Added the
WAGTAILMENUS_ADD_EDITOR_OVERRIDE_STYLES
setting to allow override styles to be disabled.
Run migrations after updating!¶
New fields have been added to MainMenu
and FlatMenu
models, so you’ll need to run the migrations for those. Run the following:
django manage.py migrate wagtailmenus
Setting max_levels
and use_specific
on your existing menus¶
Edit your existing MainMenu
and FlatMenu
objects via the Wagtail CMS.
You should see a new collapsed ADVANCED SETTINGS panel at the bottom of each form, where both of these fields live.
Default values for MainMenu
are max_levels=2
and use_specific=1
.
Default values for FlatMenu
are max_levels=1
and use_specific=1
.
Switch to using the new use_specific
options¶
If you’re passing use_specific=True
or use_specific=False
to the any
of the menu tags, you’ll need to change that to one of the following:
use_specific=USE_SPECIFIC_OFF
(oruse_specific=0
)use_specific=USE_SPECIFIC_AUTO
(oruse_specific=1
)use_specific=USE_SPECIFIC_TOP_LEVEL
(oruse_specific=2
)use_specific=USE_SPECIFIC_ALWAYS
(oruse_specific=3
)
See the following section of the README for further info: https://github.com/rkhleics/wagtailmenus/blob/v2.0.0/README.md#using-menupage
Changes to MenuPage.has_submenu_items()
and MenuPage.modify_submenu_items()
¶
If you’re extending these methods on your custom page types, you will likely need to make a few changes.
Firstly, the check_for_children
argument is no longer supplied to has_submenu_items()
, and is will no longer be accepted as a value.
Secondly, both the modify_submenu_items()
and has_submenu_items()
methods both accept an optional menu_instance
argument, which you’ll need to also accept.
See the updated section of the README for corrected code examples: https://github.com/rkhleics/wagtailmenus/blob/v2.0.0/README.md#11-manipulating-sub-menu-items-for-specific-page-types
Adding the context_processor
to settings¶
If you’re upgrading from wagtailmenus version 1.5.1
or lower, you’ll need to update your settings to include a context_processor from wagtailmenus. Your TEMPLATES
setting should look something like the example below:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(PROJECT_ROOT, 'templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.request',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.contrib.messages.context_processors.messages',
'wagtail.contrib.settings.context_processors.settings',
'wagtailmenus.context_processors.wagtailmenus',
],
},
},
]
Release notes for versions preceeding v2.0.0
can be found on Github:
https://github.com/rkhleics/wagtailmenus/releases?after=v2.0.0