New Wagtail Course! 🥳 The Ultimate Wagtail Developers Course

Tutorial Wagtail Version: 2.x

How to Create a Custom Wagtail Menu System

Almost every website has some form of navigation. Wagtail websites are no different. But creating a menu isn't as easy as making top level pages (although that's an option!). In this tutorial we're going to explore how to create a Menu System using a Clusterable Model, an Oderable, a Snippet, and a custom template tag.. from scratch!

Every good site has a navigation, and it's usually found in the header.

Wagtail already comes with an option to show pages in your menus, and all you have to do is query pages using PageName.objects.live().in_menu().

But that doesn't give you control over the placement of each page. So let's say you wanted a contact button to the very right of your menu, that's gets harder to do. You can drag and drop pages to have the proper order, and that would solve the ordering problem.

But what if you want to link to:

  • A certain part of a page, or
  • An external URL

Well then you're kind of stuck if you want either of those options. Also creating a mega menu would be pretty difficult.

Luckily Wagtail gives us all the ingredients we need to make our own custom menu system using:

  • 1 Clusterable Model
  • 1 Orderable Model
  • 1 Snippet
  • 1 Template Tag
  • and 1 loop in our template

Below you'll find the code that was used in the video.

Note: there are different files, and they all belong to an app called "menus".

``` """menus/templatetags/menus_tags.py""" from django import template from ..models import Menu register = template.Library() @register.simple_tag() def get_menu(slug): return Menu.objects.filter(slug=slug).first() ```
``` """menus/models.py""" from django.db import models from django_extensions.db.fields import AutoSlugField from modelcluster.fields import ParentalKey from modelcluster.models import ClusterableModel from wagtail.admin.edit_handlers import ( MultiFieldPanel, InlinePanel, FieldPanel, PageChooserPanel, ) from wagtail.core.models import Orderable from wagtail.snippets.models import register_snippet class MenuItem(Orderable): link_title = models.CharField( blank=True, null=True, max_length=50 ) link_url = models.CharField( max_length=500, blank=True ) link_page = models.ForeignKey( "wagtailcore.Page", null=True, blank=True, related_name="+", on_delete=models.CASCADE, ) open_in_new_tab = models.BooleanField(default=False, blank=True) page = ParentalKey("Menu", related_name="menu_items") panels = [ FieldPanel("link_title"), FieldPanel("link_url"), PageChooserPanel("link_page"), FieldPanel("open_in_new_tab"), ] @property def link(self): if self.link_page: return self.link_page.url elif self.link_url: return self.link_url return '#' @property def title(self): if self.link_page and not self.link_title: return self.link_page.title elif self.link_title: return self.link_title return 'Missing Title' @register_snippet class Menu(ClusterableModel): """The main menu clusterable model.""" title = models.CharField(max_length=100) slug = AutoSlugField(populate_from="title", editable=True) # slug = models.SlugField() panels = [ MultiFieldPanel([ FieldPanel("title"), FieldPanel("slug"), ], heading="Menu"), InlinePanel("menu_items", label="Menu Item") ] def __str__(self): return self.title ```
``` {% load menus_tags %} {# Your template; perhaps base.html #} {% get_menu "main" as navigation %} {% for item in navigation.menu_items.all %} <a href="{{ item.link }}" {% if item.open_in_new_tab %} target="_blank"{% endif %}>{{ item.title }}</a> {% endfor %} ```
Wagtailmenus Repo

Near the end of this video I briefly talk about a package called wagtailmenus that basically does all of this for you (and a little more). Here are the link:

Related tutorials

How to Enable the v2 API to Create a Headless CMS

Posted on

Wagtail comes with a lot of really powerful features. Many features are not enabled by default as to keep your site running quickly and efficiently. One of those amazing features is the Wagtail v2 API, which can return any page, image, document, orderable and StreamField as a JSON response for your SPA/PWA to consume. We'll enable this by adding just 12 lines of code to our Wagtail CMS website.

View lesson, How to Enable the v2 API to Create a Headless CMS

How to Use ListBlocks to Create Repeating StreamField Content

Posted on

Occasionally you'll want a StreamField that can have multiple repeating content areas. A good example is the design component known as a Card. In this lesson we'll explore a ListBlock to enable us to create unlimited cards with custom data, ImageChooserBlock, PageChooserBlock and how to loop through a ListBlock in your Wagtail CMS template.

View lesson, How to Use ListBlocks to Create Repeating StreamField Content

How to Add a New Wagtail Page From Scratch

Posted on

In this video we're going to explore how to add a new app to our Wagtail CMS website, how to install it, and how to add the custom page model. We'll be creating a brand new Wagtail Page from scratch.

View lesson, How to Add a New Wagtail Page From Scratch

How to Deploy Wagtail to Heroku

Posted on

Learn how to deploy a brand new Wagtail site to a free Heroku instance.

View lesson, How to Deploy Wagtail to Heroku

The Ultimate Wagtail Developers Course

This course covers everything from basic installation to advanced features like custom blocks and API integration, it's perfect for developers looking to enhance their skills with this powerful CMS.

Ultimate Wagtail Developers Course Logo