Accueil / Blog / Métier / 2016 / Wagtail : How to make your own content type models (part 1)

Wagtail : How to make your own content type models (part 1)

Par Yann Fouillat — publié 29/07/2016
We are used to initialize our CMS directly from a web interface, often including lots of complex panels. Wagtail prefers to use the Django philosophy and offers us to define our content type as models instead.
Wagtail : How to make your own content type models (part 1)


Knowledge of Django base principles is required to understand this post properly. If this is not your case, I advise you to read the introduction from the official documentation.

Wagtail Initialization

The initialization of a Wagtail website is very similar to a Django one: making its virtual environnment, install Wagtail, then use the wagtail command. You then just have to make the database and create a super user account:

$ mkvirtualenv my_wagtail
$ pip install wagtail
$ wagtail start my_wagtail
$ cd my_wagtail
$ python migrate
$ python createsuperuser

We have two apps by default: home and search. home contains a content type whereas search define a view allowing searches.


Every content type is actually a Django model inheriting from the Page model. This model already has some field by default (such as a title, a slug, ...). The default fields list is in the documentation.

The fields used are the ones from Django (CharField, DateTimeField, ...), plus some from Wagtail: RichTextField, used to write rich text (with a WYSIWYG), and StreamField, that we will see further along this post. Let's make our first content type by modifying HomePage:

class HomePage(Page):
    subtitle = models.CharField(max_length=255)
    intro = RichTextField()
    image = models.ForeignKey(

An image field is a simple ForeignKey to the wagtailimages.Image model. It is possible to link to a document using the same method by using the wagtaildocs.Document model.


A StreamField is a particular field allowing mixing other type of field in an organized way. A presentation is available on Youtube. Let's see how to make a StreamField:

class HomePage(Page):
    body = StreamField([
        ('title', blocks.CharBlock(classname="full title")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock()),

A StreamField is a list of blocks. The complete list of blocks is available in the documentation.

Good, now that we have the fields of our content type defined, we just have to choose how to show them in the edit view.



Wagtail has several panel by default to organize the fields. content_panels for the content itself, promote_panels for metadata and settings_panels for the settings:

class HomePage(Page):
    # [...]

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

A panel is a list of FieldPanels. There are several types.


FieldPanel for every basic field. FieldPanel have the optional parameter classname to add a CSS class to the field. Wagtail has several classes available:

  • title.
  • full, giving the input the full size of its container.
  • col1 to col12, telling the size the input will be.


StreamFieldPanel for StreamField fields :



The ChooserPanel to select already existing objects. There are four different one: PageChooserPanel, ImageChooserPanel, DocumentChooserPanel or SnippetChooserPanel :


How to structure fields

There are three types of FieldPanel just to structure fields:


MultiFieldPanel is used to group fields together. An accordion can be made by adding collapsible as a class name. collapsed can be added to have it collapsed by default.

content_panels = Page.content_panels + [
        heading="Un MultiFieldPanel",
        classname="collapsible collapsed",
    FieldPanel('intro', classname="full"),


FieldRowPanel is used to have several fields on the same line. It is important to precise classname with the size of the columns in order for the fields to display correctly:

content_panels = Page.content_panels + [
        FieldPanel('subtitle', classname="col6"),
        FieldPanel('intro', classname="col6"),


InlinePanel is used to handle another model, and is specifically used to make a multiple field.

InlinePanels use a specific model: Orderable (Documentation):

class HomePage(Page):

    content_panels = Page.content_panels + [
        InlinePanel('links', label="Liens")

class HomePageLien(Orderable):
    page = ParentalKey(HomePage, related_name='links')
    name = models.CharField(max_length=255)
    url = models.URLField()

    panels = [

ParentalKey is just a ForeignKey used for django-modelcluster (for more information, see the documentation)

Just like every other Django model, we first have to make and run the migrations:

$ python makemigrations
$ python migrate

Don't hesitate to read the documentation to go further.

We now can create our first contents. But there is still a last thing to understand.


Wagtail organizes contents as a tree view. Just like every tree, there is a root page by default for every site (yes, Wagtail is multisite). To this root page, we usually tie up a child page of homepage type. This is the first level. Then, from this homepage, we will have child pages of list type (the second level), and then the detail pages. As an example, we will consider that our homepage will contain a blog page which will list the blog post in preview mode. To do this, this page only accepts blog post type page as children. We can also define that the only parent page type possible in the blog post is the blog index. In order to do that, Wagtail has two attributes we can define on our page models: parent_page_types and subpage_types (Documentation).

For our homepage, we will thus create another model BlogIndex which will only accept blog post type as children:

class BlogIndex(Page):
    subpage_types = ['app.BlogPage']

And we will add another model, BlogPage, which will only allow a page of type BlogIndex as a parent, and which will not be able to have children:

class BlogPage(Page):
    parent_page_types = ['app.BlogIndex']
    subpage_types = []


You now have every tools to do your own Pages. We will see in the next post how to use this model and its manager.

Voir aussi
Django Rest Framework : les tests (partie 8) Django Rest Framework : les tests (partie 8) 22/02/2016

Avec les API REST, développer très rapidement des tests fonctionnels complets qui frôlent les ...

Formation Django initiation à Toulouse du 13 au 15 mars Formation Django initiation à Toulouse du 13 au 15 mars 26/01/2017

Entrez de plain-pied dans l'univers de Django aux côtés de développeurs ayant une expérience de ...

Python : Bien configurer son environnement de développement Python : Bien configurer son environnement de développement 07/12/2015

Comment utiliser les bonnes pratiques de développement Python.

Internationalisation avec Django Internationalisation avec Django 27/11/2018

En tant que développeurs nous sommes parfois confronté à la problématique de l'accessibilité ...

Retour sur la PyConFr 2016 Retour sur la PyConFr 2016 18/10/2016

Nous étions présents à Rennes pour PyConFr 2016. Voici notre compte-rendu à chaud.