12 Mar 2015

Getting started in Wagtail, a newcomer's perspective

Having used Drupal almost exclusively as my main tool of choice for a while now, I was asked to put together a build using Wagtail. This was to be primarily as a front end developer taking a design in browser prototype and templating it, however it ended up being a lot more than that.

Glenn Barr

Glenn Barr

Drupal Developer
Test RCA screenshot

Wagtail is an open source CMS created by Torchbox, allowing for simplicity and elegance in design, while aiming to give site editors a clear, obvious interface for maintaining good quality content.

Built on Django, it’s fast out of the box, enabling sites with high demands to be scaled efficiently and effectively.

Installation

There is a great demo box available from GitHub to view Wagtail’s features and take it for a test drive, which is well worth doing. I had previously done this, so was familiar with some of the aspects of Wagtail installation and setup. Follow the demo installation guide to try it out yourself. Beginning from the demo build wasn't relevant for this job, just thought I’d mention it.

Installing Wagtail is very straightforward and can be summarised in 4 simple steps, assuming local installation of Vagrant, Django and pip, along with some other dependencies. If you don’t have pip, try using easy_install pip.

1. Install Wagtail*

pip install wagtail

* - For Mavericks, it may be necessary to run sudo -H pip install --upgrade setuptools due to Python package issues.

2. Create new project

wagtail start project_name

3. Launch virtual machine and connect to it

cd project_name; vagrant up; vagrant ssh

4. Create superuser and spark up the built-in server (these commands are run while ssh'd in to the Vagrant box)

dj createsuperuser (following the instructions), then djrun

More detailed installation instructions can be found in Wagtail’s documentation.

Note: dj is an alias for ./manage.py.

You can now access your new site at http://localhost:8111/

Initial Setup

The design for this build has followed our design in browser process, which provided me with a good basis of expected styles, having been provided with the HTML and CSS (well, SASS to be more precise) to use.

The first thing to tackle was the different paths needed throughout the HTML and CSS to link up the various assets.

Seeing as the location of our fixed assets are found within the /static directory from the site root, using the `static` templating path made this simple.

Before: <img src=”../img/my_img.jpg” />
After: <img src=”{% static ‘img/my_img.jpg’ %}” />

Starting with models

Models are files that describe the fields that will be used within an app. Apps are collections of models and (sometimes) templates and can often be thought of as content types, as well as any specific settings that aren’t to be used globally. As shown below, models also allow you to layout how the fields are presented in the administrative area, by controlling the panels.

Note: It is possible to add new panels as necessary, as we’ve done for sites requiring multi-lingual translations.

A lot of initial model building is done by our Django developers, but I thought I’d make a start on the standard page and standard index models to get ahead of the game.

The first new app that I created was a copy/paste affair, amending the included files to make them work. Thankfully, there is a much easier way of doing this:

dj startapp my_app

(The above command is run while ssh'd in to the vagrant box.)

This creates the app directory and places all relevant files necessary for the app.

In order to have your new app included in the site, you will need to add the app name to the list of installed apps in your settings file, which can be found at <site root>/<site name>/<site name>/settings/base.py.

By way of an example, a typical standard page app might look like the following:

class StandardPage(Page):
    intro = models.TextField('Intro', blank=True)
    body = models.RichTextField('Body', blank=True)

In order to get these fields showing in the admin area, they are added to a panel:

StandardPage.content_panels = [
    FieldPanel('title', classname='full title'),
    FieldPanel('intro', classname='full'),
    FieldPanel('body', classname='full'),
]

Tip: Wagtail does provide image functionality in core, however if you need to expand your image fields to include anything further, it’s often simpler to create a new image app up front, rather than trying to retrofit it.

Migrations

Once new fields have been added to a model, a migration needs to be created to update the database. This command is run while ssh'd in to the vagrant box:

dj makemigrations

This creates the migration files, which can be run against the database, using:

dj migrate

Templating

It’s a joy to have direct control over all aspects of the output of the HTML from the start, without having to introduce things like Drupal’s Display Suite, Base themes, etc.

Having a prototype to work from sped up this process. Initially it was simple to drop in the HTML provided and ensure it was rendering correctly.

Setting up a base template

There should be a base.html file located at <site_root>/my_app_name/templates/base.html. This will initially be populated with the default Wagtail text, but should be used as your base template, to be extended by your other page templates. It will likely include your <html>, <head> and <body> elements, as well as any relevant content for them.

In the base template is where you will create the regions to be overridden by the page templates that will extend base.

{% block content %}{% endblock %}

It’s possible to have content inside this block to be used as a default, but in this case it has been left blank to be populated by other templates.

To separate out header and footer elements - includes were used:

{% include "includes/header.html" %} 
and
{% include "includes/footer.html" %}

Without going into too much more detail, there are plenty of other tags that were used, which can be found in the documentation.

Extending the standard page template

Using the example given above, outputting data in to the standard page’s template is relatively straightforward.

Begin with:

{% extends "base.html" %}

This then allows the content block to be populated:

{% block content %}
<!-- Your HTML here -->
{% endblock %}

In order to then insert data from the fields that were created in the models is a case of using the following:

{% block content %}
    <h1>{{ self.title }}</h1>
    <p class=”intro”>{{ self.intro }}</p>
    {{ self.body|richtext }}
{% endblock %}

Note: The body field doesn’t require a wrapper, as it’s output using the richtext filter. There are many filters available, which can be seen on Django’s templating documentation.

Seeing as the intro field is optional (as it was given `blank=True`), it would make sense to check if it exists before outputting its wrapper, which leads us nicely to an if statement example:

{% block content %}
    <h1>{{ self.title }}</h1>
    {% if self.intro %}
        <p class=”intro”>{{ self.intro }}</p>
    {% endif %}
    {{ self.body|richtext }}
{% endblock %}

Gotchas

When building models, it’s all too easy to omit the inclusion of necessary imports.

The example given above would require (at least) the following to be added to the top of the app's models.py file:

from django.db import models
 
from wagtail.wagtailcore.models import Page
from wagtail.wagtailcore.fields import RichTextField
from wagtail.wagtailadmin.edit_handlers import FieldPanel

Similarly, it’s important to load the correct handlers in your template files. Our standard page above is likely to need:

{% load wagtailcore_tags static compress %}

Conclusion

I’ve thoroughly enjoyed my journey into Wagtail so far. There is a lot more I could have put in this article, but wanted to give a brief overview to any budding Wagtail wannabe devs.

We’re only scratching the surface with this post, as we haven’t even started talking about things like abstracted fields, translations, orderables (repeatable fields), more logic like loops and many more.

If you want to get involved with the project, please check out the Wagtail repository from GitHub and get your hands dirty.