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

## Implementing MenuPage into your project¶

1. Subclass wagtailmenus.models.MenuPage on your model instead of the usual wagtail.wagtacore.models.Page, just like in the following example:

# appname/models.py

"""
This model will gain the fields, methods and 'setting_panels' attribute
"""
...


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

...

class GenericPage(BaseProjectPage):
...

class ContactPage(BaseProjectPage):
...

2. 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 the settings_panels attribute on a custom model to surface other custom fields in that tab, you’ll need to include additional panels to surface the new MenuPage fields in the page edit interface. Wagtailmenus includes a pre-defined menupage_panel to make this easier, which you can use like this:

# appname/models.py

"""
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'),
]
...

3. Create migrations for any models you’ve updated by running:

python manage.py makemigrations appname

4. 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

1. Subclass wagtailmenus.models.MenuPageMixin to create your model, including it to the right of any other class that subclasses Page:
# appname/models.py

from wagtail.wagtailforms.models import AbstractEmailForm

"""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'),
]
...

1. Create migrations for any models you’ve updated by running:
python manage.py makemigrations appname

1. 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 functionality 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

...

current_page, current_ancestor_ids,
current_site, allow_repeating_parents, apply_active_classes,

"""
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'
* 'request'
* 'use_absolute_page_urls'
"""

# Start by applying default modifications

base_url = self.relative_url(kwargs['current_site'])
"""
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
"""
{
'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',
},
))

"""
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'
* 'request'
"""

return True
# Resort to default behaviour


The above changes would result in the following HTML output when rendering a ContactPage instance in a main menu:

...
<li class=" dropdown">
<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 &amp; 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

):
current_site, allow_repeating_parents, apply_active_classes,

'href': '/news/',
})


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.