An app to help you manage menus in your Wagtail projects more consistently.
Admin interfaces and components for managing menus through Wagtail's admin interface. WagtailMenus provides comprehensive admin integration with inline panels for menu items, custom form classes, and admin views that integrate seamlessly with Wagtail's interface.
Main admin classes for managing menus in the Wagtail admin interface.
class MainMenuAdmin:
"""
Admin interface for main menus.
Provides the administrative interface for managing site-wide
main navigation menus through Wagtail's admin panel.
"""
model = 'MainMenu'
menu_label = 'Main menus'
menu_icon = 'list-ul'
list_display = ['site', 'max_levels']
list_filter = ['site']
class FlatMenuAdmin:
"""
Admin interface for flat menus.
Provides the administrative interface for managing custom
flat menus (footer, sidebar, etc.) through Wagtail's admin panel.
"""
model = 'FlatMenu'
menu_label = 'Flat menus'
menu_icon = 'list-ul'
list_display = ['title', 'handle', 'site']
list_filter = ['site']
search_fields = ['title', 'handle']Specialized admin views for different menu operations.
class MainMenuIndexView:
"""Index view for main menus showing all site main menus."""
template_name = 'wagtailmenus/admin/mainmenu_index.html'
class MainMenuEditView:
"""Edit view for main menu configuration and items."""
template_name = 'wagtailmenus/admin/mainmenu_edit.html'
class FlatMenuIndexView:
"""Index view for flat menus showing all flat menus."""
template_name = 'wagtailmenus/admin/flatmenu_index.html'
class FlatMenuCreateView:
"""Create view for new flat menus."""
template_name = 'wagtailmenus/admin/flatmenu_create.html'Inline panel classes for managing menu items within menu admin interfaces.
class MenuItemInlinePanel:
"""
Base inline panel for menu items.
Provides the foundation for inline editing of menu items
within menu admin interfaces.
"""
panels = [
# Standard menu item editing panels
]
class MainMenuItemsInlinePanel(MenuItemInlinePanel):
"""
Inline panel for main menu items.
Allows editing of main menu items directly within
the main menu admin interface.
"""
model = 'MainMenuItem'
heading = 'Menu items'
class FlatMenuItemsInlinePanel(MenuItemInlinePanel):
"""
Inline panel for flat menu items.
Allows editing of flat menu items directly within
the flat menu admin interface.
"""
model = 'FlatMenuItem'
heading = 'Menu items'Pre-configured panel layouts for different menu types and components.
# Main menu panel configurations
main_menu_content_panels: list
"""Content panels for main menu editing interface."""
flat_menu_content_panels: list
"""Content panels for flat menu editing interface."""
menu_settings_panels: list
"""Settings panels for menu configuration options."""
# Page-related panel configurations
linkpage_edit_handler: object
"""Edit handler for link pages in admin interface."""
menupage_settings_panels: list
"""Settings panels for menu-enabled pages."""Custom form classes for menu administration with validation and enhanced functionality.
class FlatMenuAdminForm:
"""
Admin form for flat menus.
Provides validation and custom functionality for flat menu
creation and editing in the admin interface.
"""
class Meta:
model = 'FlatMenu'
fields = '__all__'
def clean_handle(self):
"""Validate flat menu handle uniqueness and format."""
def clean(self):
"""Perform cross-field validation for flat menu data."""
class LinkPageAdminForm:
"""
Admin form for link pages.
Provides validation for link pages ensuring proper URL
formatting and required field completion.
"""
class Meta:
model = 'AbstractLinkPage'
fields = '__all__'
def clean_link_url(self):
"""Validate link URL format and accessibility."""
class SiteSwitchForm:
"""
Form for switching between sites in multi-site setups.
Allows administrators to switch context between different
sites when managing menus in multi-site installations.
"""
site: 'Site' # Site selection field
def __init__(self, *args, **kwargs):
"""Initialize form with available sites."""# In your wagtail_hooks.py or similar
from wagtail import hooks
from wagtailmenus.menuadmin import MainMenuAdmin, FlatMenuAdmin
# Admin classes are automatically registered by wagtailmenus
# But you can customize them:
@hooks.register('register_admin_menu_item')
def register_custom_menu_admin():
return MainMenuAdmin()from wagtailmenus.menuadmin import FlatMenuAdmin
from wagtailmenus.models import FlatMenu
class CustomFlatMenuAdmin(FlatMenuAdmin):
"""Custom flat menu admin with additional functionality."""
list_display = ['title', 'handle', 'site', 'created_at']
list_filter = ['site', 'created_at']
search_fields = ['title', 'handle', 'heading']
def get_queryset(self, request):
"""Filter menus based on user permissions."""
qs = super().get_queryset(request)
if not request.user.is_superuser:
# Limit to current site for non-superusers
current_site = request.site
qs = qs.filter(site=current_site)
return qsfrom wagtailmenus.panels import MenuItemInlinePanel
from wagtail.admin.panels import FieldPanel, InlinePanel
class CustomMenuItemInlinePanel(MenuItemInlinePanel):
"""Custom menu item inline with additional fields."""
panels = [
FieldPanel('link_text'),
FieldPanel('link_page'),
FieldPanel('link_url'),
FieldPanel('allow_subnav'),
FieldPanel('custom_css_class'), # Additional custom field
FieldPanel('icon'), # Additional custom field
]
extra = 0
min_num = 1
# Use in your menu model
class CustomFlatMenu(AbstractFlatMenu):
content_panels = [
FieldPanel('title'),
FieldPanel('handle'),
FieldPanel('heading'),
InlinePanel('menu_items', panels=CustomMenuItemInlinePanel.panels),
]from wagtailmenus.forms import FlatMenuAdminForm
from django import forms
class CustomFlatMenuForm(FlatMenuAdminForm):
"""Custom form with additional validation."""
def clean_handle(self):
"""Custom handle validation."""
handle = self.cleaned_data.get('handle')
if handle:
# Ensure handle follows custom naming convention
if not handle.startswith('custom-'):
raise forms.ValidationError(
"Handle must start with 'custom-'"
)
# Check for conflicts with other handles
if handle in ['reserved', 'admin', 'api']:
raise forms.ValidationError(
"This handle is reserved and cannot be used"
)
return handle
def clean(self):
"""Cross-field validation."""
cleaned_data = super().clean()
title = cleaned_data.get('title')
handle = cleaned_data.get('handle')
if title and handle:
# Ensure handle relates to title
if not any(word in handle for word in title.lower().split()):
self.add_error('handle',
'Handle should relate to the menu title')
return cleaned_data
# Use custom form in admin
class CustomFlatMenuAdmin(FlatMenuAdmin):
form = CustomFlatMenuFormfrom wagtail.admin.panels import FieldPanel, InlinePanel, TabbedInterface, ObjectList
# Custom panel layout for main menus
custom_main_menu_panels = [
FieldPanel('site'),
FieldPanel('max_levels'),
FieldPanel('use_specific_templates'),
InlinePanel('menu_items', panels=[
FieldPanel('link_text'),
FieldPanel('link_page'),
FieldPanel('link_url'),
FieldPanel('allow_subnav'),
]),
]
# Tabbed interface for complex menus
class AdvancedFlatMenu(AbstractFlatMenu):
edit_handler = TabbedInterface([
ObjectList(flat_menu_content_panels, heading='Content'),
ObjectList(menu_settings_panels, heading='Settings'),
ObjectList([
FieldPanel('seo_title'),
FieldPanel('search_description'),
], heading='SEO'),
])from wagtailmenus.forms import SiteSwitchForm
from django.shortcuts import render, redirect
def admin_menu_view(request):
"""Admin view with site switching capability."""
if request.method == 'POST':
form = SiteSwitchForm(request.POST)
if form.is_valid():
selected_site = form.cleaned_data['site']
# Store selected site in session
request.session['admin_site_id'] = selected_site.id
return redirect('wagtailmenus_mainmenu_index')
else:
# Initialize with current site
current_site = getattr(request, 'site', None)
form = SiteSwitchForm(initial={'site': current_site})
return render(request, 'admin/site_switch.html', {
'form': form,
'current_site': current_site,
})# Admin class attributes
class AdminTypes:
model: str | type
menu_label: str
menu_icon: str
list_display: list[str]
list_filter: list[str]
search_fields: list[str]
# Panel configuration types
class PanelTypes:
main_menu_content_panels: list
flat_menu_content_panels: list
menu_settings_panels: list
linkpage_edit_handler: object
menupage_settings_panels: list
# Form field types
class FormTypes:
handle: str
link_url: str
site: 'Site'Install with Tessl CLI
npx tessl i tessl/pypi-wagtailmenus