Wagtail Photography 7

Finally we are building our menus. But first, I want you to quickly add a Projects page, add a few SubProject children to it as well as two or three SimpleSubProject pages. In terms of hierarchy:

  • Home
    • Projects
      • SubProject 1
      • SubProject 2
    • Simple SubProject 1
    • Simple SubProject 2

Notice how Projects and Simple SubProject are children of the home and SubProjects are children of the Projects page. You can either add some random text and photos or nothing to those pages. They just need to exist (and have a main_image, you’ll see that it is required in the admin interface).

How do we create menus? We use wagtailmenus. Install it via pip:

pip install wagtailmenus

Then we need to go to our portfolio/settings/base.py file and add the following to the INSTALLED_APPS list.

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

Then, in the same file, under context_processors in the TEMPLATES list, we need to add another line:

TEMPLATES = [
    {
        'BACKEND': ...,
        'DIRS': ...,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                ...,
                'wagtailmenus.context_processors.wagtailmenus',
            ]
        }
    }
]

Save and run migrations in order to create the database tables for wagtailmenus.

python manage.py migrate wagtailmenus

Now run the server again and go to the admin interface, log in. Then on the left sidebar click on Settings and you’ll see we have a Main Menu link. Before we can do that though, you first have to go edit the pages that you’d like to have the option of adding to the Menu by going to each page, clicking edit, and under the Promote tab make sure you tick the “Show in menus” option. Do that for the pages you’d like to add to the menu and then come back to Settings/Main Menu.

Then, I clicked “Add Menu Item” and choose the pages I’d like to have in my menu. Please note that I chose the Home, Projects and and Simple SubProject pages. I did not add the SubProject 1 or SubProject 2 to my menu. Instead, I ticked/selected “Allow sub-menu for this item:” for the Projects page. Click save and now let’s add this information to the template.

In the portfolio/templates directory let’s create a “menus” directory. In there, let’s create a “main_menu.html” file and now let’s think of what we want the menu to have. We want a menu with some dropdown menus for some of the links but we want those links to be accessible as well (i.e. I want to be able to access both the Projects page and the SubProject pages). So first of all, in our base.html file, I add another import and change my <nav> </nav> to the following:

...
{% load menu_tags %}
...
  <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container-fluid">
      <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavDropdown"
        aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse flex-row-reverse" id="navbarNavDropdown">
        <ul class="navbar-nav">

          {% main_menu max_levels=2 template="menus/main_menu.html" add_sub_menus_inline=True %}

        </ul>
      </div>
    </div>
  </nav>
...

Notice I am first importing menu_tags to be able to deal with my wagtailmenus extension. Then, rather than listing individual links, I create a main_menu with a max_levels of 2 (i.e. projects/subprojects) that uses the “menus/main_menu.html” template and also adds the sub_menus_inline (for example for drop downs). Then in my “menus/main_menu.html” file I have the following:

{% load menu_tags %}

{% for item in menu_items %}
    {% if item.sub_menu %}
        <div class="btn-group">
            <a class="btn-link nav-link bg-light" href="{{item.href}}">{{item.text}}</a>
            <button type="button" class="btn-link nav-link dropdown-toggle dropdown-toggle-split bg-light"
                style="border: none; outline: none;" id="dropdownMenuReference" data-bs-toggle="dropdown" aria-expanded="false"
                data-bs-reference="parent">
                <span class="visually-hidden">Toggle Dropdown</span>
            </button>
            <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="dropdownMenuReference">
                {% for sub_item in item.sub_menu.items %}
                    <li><a class="dropdown-item {{sub_item.active_class}}" href="{{sub_item.href}}">{{sub_item.text}}</a></li>
                {% endfor %}
            </ul>
        </div>
    {% else %}
        <li class="nav-item">
            <a class="nav-link" href="{{item.href}}">{{item.text}}</a>
        </li>
    {% endif %}
{% endfor %}

We load our tags, we need them. Then I iterate over each of the item in the menu_items. If it has a sub_menu (i.e. children), we have one block of code. If not, it is simply a nav-item in our nav-bar where the anchor tag has a nav-link class. In there, we can specify its href with {{item.href}} and its text with {{item.text}}. Pretty simple no? If the menu item does have a sub-menu, I used some bootstrap components (links and split buttons) to essentially have a link/button that allows both the main link to go to the parent page (i.e. Projects) but also to have a button next to it that, when clicked, shows the dropdown menu (not too pretty at the moment, we’ll work on styling later). Notice we first have an achor tag with the main item href and text as well as a dropdown button. Then, we have an unordered list for our sub_items. We iterate for the item.sub_menu.items and for each of those we create a list element with an anchor tag. Now if we go to our main page, we should see our menu working as intended (if somewhat not too pretty).

In the next post we will add a few more elements to our site’s navigation: editable footer, social links, maybe footer image, etc. Stay tuned!