wagtail_menus_header.jpg

Wagtail Tutorial #12: How to Create and Manage Menus of Wagtail application

Last updated on by MichaelYin

Introduction

The menu is an essential component for most CMS, with Wagtail, we can build powerful menu component as we like. But right now there is no good tutorial talking about this feature with Wagtail, so I decided to write this article to help people. If you have any problem about Wagtail, feel free to contact me.

In this Wagtail tutorial, we would learn how menu in Wagtail works, and how to create a powerful menu using wagtailmenus package.

The code in this tutorial work with Wagtail 2 (The latest stable Wagtail version), but you can also change the import statement to make it work with Wagtail 1.

Prerequisites

If you have an existing Wagtail project, that would be good to go, if you are new to Wagtail. I recommend you to check wagtail-bootstarap-blog , this project is developed exclusively for my Wagtail tutorial series, it is a standard blog which is based on Bootstrap theme and Wagtail.

We would use python shell to help us inspect some object, so IPyhon is recommended to install in your env. You can do this by using command pip install ipython

Step 1 — Inspect Page object in Wagtail

Next lets' first start to inspect page objects of our wagtail project, we can do this by entering Django shell.

Django shell is just like the Python prompt, but with some additional Django magic. :) You can use all the Python commands here too

# cd to root directory of your Wagtail project
$ python manage.py shell

Python 3.5.0 (default, Aug 17 2017, 10:54:23)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]:

Now we entered the Django shell, and we can write code and check the output at the same time.

We try to check the detail of Page model in Wagtail, and find out there is something interesting, you can use the same method to check other fields of Wagtail Page.

In [1]: from wagtail.core.models import Page
In [2]: Page?
Init signature: Page(*args, **kwargs)
Docstring:      Page(id, path, depth, numchild, title, draft_title, slug, content_type, live, has_unpublished_changes, url_path, owner, seo_title, show_in_menus, search_description, go_live_at, expire_at, expired, locked, first_published_at, last_published_at, latest_revision_created_at, live_revision)
File:           ~/.pyenv/versions/wagtail_projet/lib/python3.5/site-packages/wagtail/core/models.py
Type:           PageBase

The ? behind Page model is an built-in operation from IPython, it can help us quickly get the detail of any object of Python.

As we can see from the output above, there is a field show_in_menus in Wagtail Page definition, if you check the Wagtail admin, you can find out that it is in Promote tab.


So if we want to make some pages to display in menu component, we need set the show_in_menus value to True, then write code to filter them and render the pages in template.

Step 2 — Basic Menu Implementation

Now we try to implement a very basic top menu in our blog. It would display some specific pages such as about, welcome pages.

First, we try to add code get_context of our BlogPage model,

def get_context(self, request, *args, **kwargs):
    context = super(BlogPage, self).get_context(request, *args, **kwargs)
    context['posts'] = self.posts
    context['blog_page'] = self

    context['menuitems'] = self.get_children().filter(
        live=True, show_in_menus=True)

    return context

What you should notice is the menuitems here, we use Django query to help us find the all live, menuable pages in the blog.

Next, we try to print the menu out in our template. So the template should look like this.

<ul class="nav navbar-nav">
    {% for menu in menuitems %}
        <li>
            <a href="{{menu.url}}">{{menu.title}}</a>
        </li>
    {% endfor %}
</ul>

If you do it in wagtail-bootstrap-blog, so the effects should look like this. If you do not see any links in the menu, please make sure you have set the show_in_menus of page to True.


Step 3 — Change page order in Wagtail menu

Wagtail has built-in support for page order, so if you want to change the link order in the menu, just change the page order in Wagtail admin.


After you are done with the page order, then refresh the blog page, link order should also be modified as well.

Step 4 — Install wagtailmenus

Now you have a good understanding of how menu in Wagtail works, so I'd like to give you a better solution for you to build menu in your Wagtail project.

It is wagtailmenus

Here I'd like to show you how to import it into our Wagtail project to generate the top menu for us.

pip install wagtailmenus

Then add settings below to the INSTALLED_APPS of your project settings.

INSTALLED_APPS = [
    ...
    'wagtail.contrib.modeladmin',  # Don't repeat if it's there already
    'wagtailmenus',
]

Then add wagtailmenus.context_processors.wagtailmenus to the context_processors in your TEMPLATES. So your template settings would look like this.

'context_processors': [
    'django.template.context_processors.debug',
    'django.template.context_processors.request',
    'django.contrib.auth.context_processors.auth',
    'django.contrib.messages.context_processors.messages',

    'wagtailmenus.context_processors.wagtailmenus',
],

After all config done, run python manage.py migrate wagtailmenus to config the database.

If you meet problem in this step, please check the wagtailmenus official doc for more detail

Step 5 — Render menu using wagtailmenus

Now we start to use wagtailmenus to help us build a top menu. It would contain links of the blog post, and some external link (surprise? ^_^).

We enter to our Wagtail admin, then goto /settigns/main menu/

In this page we can choose page from our Wagtail application, or insert external link, and change the link text of our menu.


After we config the menu, now we start to modify our template to render the data of the menu.

Edit template blog/templates/blog/base.html

{% load menu_tags %}


<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
    <div class="container">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="{{ blog_page.url }}">{{ blog_page.title }}</a>
        </div>

        {% main_menu template="menus/bootstrap3/main_menu_dropdown.html" %}

        <!-- /.navbar-collapse -->
    </div>
    <!-- /.container -->
</nav>

There are some things you can notice here.

  1. {% load menu_tags %} to make Django tag in wagtailmenus can be found.
  2. main_menu is used to render menu we just created, the template parameter is to how menu is generated. wagtailmenus has built-in templates for bootstrap3, you can modify it as you like.
  3. wagtailmenus has many powerful Django tag for us to render menu in a quick way. Main menu is supposed to generate the most important menu for you and the menu data is saved in db. If you want to generate menu from Wagtail pages dynamically, you can check section_menu or children_menu

The final result of our blog would look like this


Conclusion

In this Wagtail tutorial, we've learne how menu in Wagtail work and how to build powerful menu using wagtailmenus package

Now, where should you go next? Here are some suggestions.

  • You can start importing wagtailmenus to your Wagtail project for better menu management.
  • You can checkout wagtail-bootstrap-blog to get the source code of this Wagtail tutorial
  • If you want to know more about Wagtail, you can check my Wagtail Tutorial Series

If you have any question about this article or Wagtail, please contact and I would like to help.

Wagtail Ebook

For people who like to read ebook instead of blog posts, I have published a book on leanpub,where you can get pdf, epub, mobi version of this Wagtail book Build Blog With Wagtail CMS.


Hire Me For Your Project

My hourly rate is $30 per hour, I can help you solve specific Wagtail/Django, Scrapy problem and provide 1:1 programming help.

Contact Me For a FREE Evaluation