This blog post explains how we at Codista approach the task of creating Wagtail pages programmatically. It is a deep-dive of our blog post about initial data for Wagtail projects and focuses solely on the task of automatically creating the Wagtail page tree.

The problem

Each team member who wants to contribute to your Wagtail/Django project needs to set it up on their computer. As we described in this blog post, this can get cumbersome and time consuming quite fast. Especially if you want to provide your teammates with predefined content and a standardized page tree so they can start working right away.

The solution

Since we all love to automate repetitive and boring tasks, you probably guessed how we will approach this situation: we can programmatically create all these CMS pages from scratch. So let's dive right in.

The solution outlined below demonstrates the steps necessary to setup an entire page tree, starting with the root node. Note that this example is a i18n solution for Wagtail as described in our blog post about wagtail internationalization. That's why we start with a LanguageRedirectionPage, which is responsible for redirecting the user to the current language's page tree.

Creating the root page

from django.contrib.contenttypes.models import ContentType
from wagtail.core.models import Page

# Create the base language redirection page which is responsible
# for redirecting the user to the, language specific, homepages
root = Page.get_first_root_node()
# Again, the LanguageRedirectionPage comes from the blog post
# linked to above.
language_redirection_page_content_type = ContentType.objects.get_for_model(
    LanguageRedirectionPage
)
# The source for model can be found here:
# https://www.codista.com/en/blog/wagtail-multi-language-and-internationalization/
language_redirection_page = LanguageRedirectionPage(
    title="codista.com",
    draft_title="codista.com",
    slug="root",
    content_type=language_redirection_page_content_type,
    show_in_menus=True
)
root.add_child(instance=language_redirection_page)
# Create a site with the new LanguageRedirectionPage set as the root
# Note: this is the wagtail Site model, not django's
Site.objects.create(
    hostname="localhost",
    root_page=language_redirection_page,
    is_default_site=True,
    site_name="codista.com",
)

We want to especially draw your attention to the Page.get_first_root_node() call. With this call you can retrieve the fixed root of the page tree, a level above all of the editable pages. This should always be your starting point of programmatically creating your page tree.

When your first editable page (in our case, that's the language redirection page) is set up, you are ready to programmatically create the rest of your wagtail tree. Let's continue here with an example of creating a HomePage.

Create the content pages

# A smaller excerpt from https://github.com/cod1sta/snippets/blob/master/blog/create-wagtail-pages-programmatically/setup_page_tree.py
from django.conf import settings
from django.contrib.contenttypes.models import ContentType

from codista.cms.models import (
    HomePage,
    LanguageRedirectionPage
)

parent_page = LanguageRedirectionPage.objects.first()
homepage_content_type = ContentType.objects.get_for_model(
     HomePage
)
# For each supported language, create a new homepage
for language_code, label in settings.LANGUAGES:
    if language_code == "de":
        title = "Home - Deutsch"
        hero_title = "Wir sind Codista."
        hero_intro = "Eine Software-Agentur, die sich auf die Entwicklung von maßgeschneiderten Websites und Apps spezialisiert hat."
        # …
    elif language_code == "en":
        title = "Home - English"
        hero_title = "We are Codista."
        hero_intro = "A software studio focused on creating custom-made websites and apps. "
        # …
    else:       
        raise RuntimeError(f"Unsupported language encountered: {language_code}")

    homepage = HomePage(
        language=language_code,
        title=title,
        draft_title=title,
        slug=language_code,
        hero_title=hero_title,
        hero_intro=hero_intro,
        show_in_menus=True,
        content_type=homepage_content_type,
    )
    parent_page.add_child(instance=homepage)

We wrap this script in a Django management command called setup_page_tree. You can find the entire management command, used to setup the codista.com website, on our Github.

Takeaways

Investing some time in automating the task of creating your page tree pays off because:

  • you will save tons of time in the long run.
  • you standardize the setup of the page tree. More standards, less bugs.

Note:

When we started programmatically creating page trees, we did not know about the functionality provided by Page.get_first_root_node(). We had no idea that there is a root node above our editable pages in Wagtail, and used an approach of creating a root page using treebeard properties directly. Don't do this! It is bad and it breaks your tree.

If you want to learn more about this issue you can read about it in the extraordinary helpful Wagtail Slack support channel or check out this commit fixing the problem.

Other Posts

  • How I became a member of Codista

    My “6 month anniversary” at Codista is coming up and I can’t really wrap my head around the fact that it’s already been that long. My name is Angela and I’m the newest member of the Codista team.