When we get a website design from a designer, we have to figure out how to implement that design in the code. Sometimes this means that we need to create custom page models or custom page templates to bring the design to life for a site. One of the pages that the client wanted would need to pull in child page content, loop through it, and alternate the background color and content placement. How could we make this happen?

Basic Setup

This tutorial assumes that you have some experience with Django and Wagtail.

For this tutorial, we are assuming that you have already created a blog app for your Wagtail project with models for a Blog Index Page and for the Blog Page, as well as corresponding templates for each model. The models.py will look something like this:

from django.db import models

from wagtail.core.models import Page
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.search import index


class BlogIndexPage(Page):
    intro = RichTextField(blank=True)

    subpage_types = ['BlogPage']

    content_panels = Page.content_panels + [
        FieldPanel('intro', classname="full")
    ]

    template = "blog/blog_index_page.html"

class BlogPage(Page):
    date = models.DateField("Date of Post")
    intro = models.CharField(max_length=250)
    body = RichTextField(blank=True)

    parent_page_type = ['BlogIndexPage']

    search_fields = Page.search_fields + [
        index.SearchField('intro'),
        index.SearchField('body'),
    ]

    content_panels = Page.content_panels + [
        FieldPanel('date'),
        FieldPanel('intro'),
        FieldPanel('body', classname="full"),
    ]

    template = "blog/blog_page.html"

Not sure how to do that? Start with Wagtail's beginner tutorial found here.

We are going to modify the Blog Index Page template. Once we have made migrations, migrated and started our local server, we need to log into our admin, publish a simple Blog Index Page, then publish three to four Blog Pages as child pages. Remember, unless you specify differently, any page type can be a child page of another page.

In your code editor, navigate to the template for your Blog Index Page. We are going to create a simple alternating block to display the child posts.

Creating the Alternating Blocks

Let's say that we want to alternate the color and size of the title of each blog post, and we want the intro to alternate between italicized and bold text. We could also alternate between entirely different layouts for displaying the title and intro for each post if we wanted, using the same method discussed below, but we will keep it simple for demonstration purposes.

Our Blog Index Template currently looks like this:

{% load wagtailcore_tags %}

{% block content %}
    {% for post in page.get_children %}
        <a href="{% pageurl post %}">{{ post.title }}</a>
        {{ post.specific.intro }}
        {{ post.specific.body|richtext }}
    {% endfor %}

{% endblock %}

All this is doing is grabbing the child pages of the Blog Index Page and looping through each one to display the link and title of the post, its intro and its body content. This is the section that we are going to change.

For a refresher on Django templating language and the for tag, see Built-In Template Tags: For.

We need to be able to control the loop if we want to alternate the content and styles. Let's say that we only need to have two separate styles for the child page display blocks. We would need to control our loop by dividing in two so that it alternates between the two choices. This can be accomplished by using these built-in variables and filters for the for loop: forloop.counter with the divisibleby filter.

{% block content %}

    {% for post in page.get_children %}
    {% if forloop.counter|divisibleby:"2" %}   

    {% else %}

    {% endif %}

    {% endfor %}

{% endblock %}

Our template is now set up for us to add the alternating blocks! Our initial loop needs to have a blue H2 title and an italicized intro, while the next loop needs to have a green H1 title and a bold intro. The initial loop will actually go after the {% else %} tag because the first part of the if statement must be divisible by 2. What does this look like in our template?

{% block content %}

    {% for post in page.get_children %}
    {% if forloop.counter|divisibleby:"2" %}   

        <h1><a href="{% pageurl post %}" style="color:green;">{{ post.title }}</a></h1>
        <strong>{{ post.specific.intro }}</strong>

    {% else %}

        <h2><a href="{% pageurl post %}" style="color:blue;">{{ post.title }}</a></h2>
        <em>{{ post.specific.intro }}</em>

    {% endif %}

    {% endfor %}

{% endblock %}

Okay, it's not exactly pretty but you can see how this works. The key is to control the loop so that it knows to alternate between the styles or content that you specify for your index page. You can use this technique for alternating between layouts, like a three-column layout vs. a two-column layout or changing the order of content with each loop.

NOTE: For an even simpler method for alternating CSS properties for your content in a loop, consider using the {% cycle %} tag. Example: <h1 class="{% cycle red-class green-class %}">. It will alternate between the classes with each loop. This is, of course, if you don't need a more fine-tuned method for alternating your blocks.