How to write Wagtail page template


Michael Yin

Full Stack Developer

Last updated on Jun 18 2021


Table of Contents

Objectives

By the end of this chapter, you should be able to:

  1. Write Django templates for the BlogPage and PostPage
  2. Learn some Django template syntax such as for loop
  3. Understand how to debug Django template with PDB

Template name

Below are some notes about the template name of the page models.

  1. If we set template variable for page model, Wagtail would use it.
  2. If we set ajax_template variable, Wagtail would use it for Ajax requests.
  3. If we do not set template, Wagtail would get template using code cls.template = "%s/%s.html" % (cls._meta.app_label, camelcase_to_underscore(name)) (So blog.BlogPage have template blog.blog_page.html)

Considering BlogPage and PostPage already exists in Wagtail admin, next, we will write templates to display the page data.

BlogPage

Create wagtail_bootstrap_blog/templates/blog/blog_page.html

{% load wagtailcore_tags %}

<h1>{{ page.title }}</h1>

<div class="intro">{{ page.description }}</div>

{% for post in page.get_children %}
    <h2><a href="{% pageurl post %}">{{ post.title }}</a></h2>
{% endfor %}

Notes:

  1. In this project, we would put all templates in the wagtail_bootstrap_blog/templates directory.
  2. In the template, We use {{ page.title }} to render the page title.
  3. We use page.get_children to iterate all child pages of the blog page. Django For loop
  4. We can also put the templates in the Django blog app. (With APP_DIRS set to True, the template loader will look in the app’s templates directory and find the templates.)

PostPage

{% load wagtailcore_tags wagtailimages_tags %}

<h1>{{ page.title }}</h1>

{% image page.header_image original %}

  <div class="tags">
    <h3>Tags</h3>
    {% for tag in page.tags.all %}
      <button type="button">{{ tag }}</button>
    {% endfor %}
  </div>

<h3>Categories</h3>
<ul>
  {% for postpage_category in page.categories.all %}
    <li>
      {{ postpage_category.blog_category.name }}
    </li>
  {% endfor %}
</ul>

<p><a href="{{ page.get_parent.url }}">Return to blog</a></p>

Notes:

  1. The syntax is very similar with the blog_page.html
  2. {% image page.header_image original %} is a Django template tag from Wagtail which can help us generate the img tag. You can check Wagtail doc: Using images in templates to learn more.

Now we already learned how to display the page data in the Django template.

Next, we will import Bootstrap theme to our project to make the pages have good style.

DEBUG with PDB

The module pdb defines an interactive source code debugger for Python programs. It supports setting (conditional) breakpoints and single stepping at the source line level, inspection of stack frames, source code listing, and evaluation of arbitrary Python code in the context of any stack frame

Even we can add PDB in Python code to make that work, but how to make it work with Django template?

django-extensions contains a template tag which can let us use PDB in Django template.

Add django-extensions to requirements.txt

Django>=3.1,<3.2
wagtail>=2.11,<2.12
psycopg2-binary
django-extensions==3.1.0

Add django_extensions to the INSTALLED_APPS in wagtail_bootstrap_blog/settings/base.py

Update docker-compose.yml

services:
  web:
    build:
      context: .
      dockerfile: ./compose/local/web/Dockerfile
    image: wagtail_bootstrap_blog_web
    command: /start
    volumes:
      - .:/app
    ports:
      - 8000:8000
    stdin_open: true
    tty: true
    env_file:
      - ./.env/.dev-sample
    depends_on:
      - db

Notes:

  1. Add stdin_open: true
  2. Add tty: true
$ docker-compose build
$ docker-compose up -d
$ docker-compose logs -f

web_1  | Django version 3.1.4, using settings 'wagtail_bootstrap_blog.settings.dev'
web_1  | Starting development server at http://0.0.0.0:8000/
web_1  | Quit the server with CONTROL-C.

In a new terminal, run command below

$ docker-compose ps
           Name                         Command               State           Ports
--------------------------------------------------------------------------------------------
wagtail_project_db_1         docker-entrypoint.sh postgres    Up      5432/tcp
wagtail_project_web_1        /entrypoint /start               Up      0.0.0.0:8000->8000/tcp

# attach the web container
$ docker attach wagtail_project_web_1

Now the shell is ready to go.

Let's try to update wagtail_bootstrap_blog/templates/blog/blog_page.html

{% load wagtailcore_tags debugger_tags %}

{{ page|pdb }}

<h1>{{ page.title }}</h1>

<div class="intro">{{ page.description }}</div>

{% for post in page.get_children %}
  <h2><a href="{% pageurl post %}">{{ post.title }}</a></h2>
{% endfor %}

Notes:

  1. At the top, we import debugger_tags from django-extensions
  2. We add {{ page|pdb }} to the template to pass page instance to the PDB shell.

Now if we visits http://127.0.0.1:8000, we will see output in the attached terminal

> /usr/local/lib/python3.8/site-packages/django_extensions/templatetags/debugger_tags.py(30)pdb()
-> return obj
(Pdb)

Now we are in PDB, and we can run some code to check the page instance

(Pdb) obj
<BlogPage: BlogPage>
(Pdb) obj.title
'BlogPage'
(Pdb) obj.get_children()
<PageQuerySet [<Page: PostPage1>, <Page: MarkDown Example>, <Page: PostPage3>, <Page: PostPage4>]>
(Pdb) c

To exit the PDB, type c (short for continue)

Notes:

  1. We can also add PDB code to the Python code to debug without IDE
  2. If you are new to PDB, you can check Python Debugging With Pdb

Michael Yin

Michael is a Full Stack Developer from China who loves writing code, tutorials about Django, Wagtail CMS and React.

He has published some ebooks on leanpub and tech course on testdriven.io.

He is also the founder of the AccordBox which provides the web development services.


Table of Contents

This book will teach you how to build a SPA (single-page application) with React and Wagtail CMS

Read More

Subscribe

Get notified about new great Web Development Tutorial