Wagtail

Débuter avec le CMS Wagtail

6 Déc 2021 | Django | 5 commentaires

Wagtail est un CMS basé sur le framework Django, lequel est écrit en Python. Dans cet article je vais vous présenter comment mettre en place Wagtail et créer la page d’accueil de notre site.

La première chose à faire est de mettre en place l’environnement de développement sur notre poste de travail. Pour ce faire nous allons utiliser un environnement virtuel proposé par Python, pour éviter de mélanger les librairies que nous allons utiliser avec vos autres projets.

Dans une deuxième phase nous allons installer et configurer Wagtail pour qu’il soit prêt à recevoir notre site.

Installation de l’environnement de développement

Je développe toujours depuis un environnement Linux (Ubuntu et Debian), les commandes qui suivent sont donc plus orientées pour ces OS, mais ils ne devraient pas beaucoup différer pour Windows ou MacOS.

Commençons par créer un dossier au nom de notre projet dans lequel nous installons l’environnement virtuel :

mkdir -p ~/projets/mon_site
cd ~/projets/mon_site
python3 -m venv env

Une fois l’environnement créé (un nouveau dossier nommé env est apparu), nous devons l’activer avec la commande suivante :

source env/bin/activate

Sous Windows il faut utiliser la commande suivante :

.\env\Scripts\activate

Installation de Wagtail

Nous installons Wagtail, et tous les packages qui lui sont nécessaires, avec la commande suivante (vérifiez toujours avant que vous êtes bien dans votre environnement virtuel, sinon les packages seront mélangés avec ceux de votre OS) :

pip install wagtail

Une fois installé, Wagtail dispose d’une commande similaire à celle de Django django-admin startproject pour générer un nouveau site/projet :

mkdir app
wagtail start mon_site app

Nous avons ainsi installé tous les répertoires et fichiers nécessaires à Wagtail dans un sous-répertoire nommé app afin de séparer les données du site avec les autres outils dont nous aurons besoin plus tard (par exemple pour « Dockeriser » notre site et pour le déploiement en production).

On se déplace ensuite dans notre répertoire nouvellement créé et nous procédons aux étapes de mise en place nécessaires à tout projet Django.

Pour commencer, on met à jour les dépendances du projet :

cd app
pip install -r requirements.txt

On applique ensuite les migrations à la base de données et on crée un super-utilisateur :

python manage.py migrate
python manage.py createsuperuser

Finalement nous lançons le serveur local comme pour chaque projet Django :

python manage.py runserver

Notre application est dès lors disponible à l’url http://localhost:8000/ et se présente comme suit :

Page d’accueil par défaut de Wagtail

Comme vous pouvez le voir, la page d’accueil du site est une page provisoire qui nous indique que notre installation fonctionne bien. Nous allons voir dans la suite de cet article comment procéder pour créer une page d’accueil sur mesure.

L’admin de Wagtail est disponible à l’url http://localhost:8000/admin/ et nous pouvons nous y connecter avec le super-utilisateur créé précédemment.

Connexion à l’admin de Wagtail
Interface de l’admin de Wagtail

S’agissant d’un CMS basé sur Django, l’admin de Django reste disponible à l’url http://localhost:8000/django-admin/.

Création de la page d’accueil du site

Lorsque nous avons installé Wagtail, celui-ci nous a automatiquement créé une app nommée home. C’est dans cette app que nous allons gérer notre page d’accueil ainsi que les pages simples de notre site (par exemple, page « À propos », « Contact », etc.).

Nous allons commencer par créer le modèle de notre page d’accueil. Pour ce faire, nous ouvrons le fichier app/home/models.py et nous y ajoutons le code suivant :

from django.db import models

from wagtail.core.models import Page
from wagtail.core.fields import StreamField
from wagtail.core import blocks
from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel
from wagtail.images.blocks import ImageChooserBlock


class HomePage(Page):
    body = StreamField([
        ('paragraph', blocks.RichTextBlock(
            label='Paragraphe', features=['h1', 'h2', 'h3', 'bold', 'italic', 'link', 'hr']
        )),
        ('image', ImageChooserBlock()),
    ], verbose_name='Contenu', null=True)

    content_panels = Page.content_panels + [
        StreamFieldPanel('body', classname="full"),
    ]

    class Meta:
        verbose_name = "page d'accueil"

Nous aurions pu n’utiliser qu’un RichTextField (un éditeur de texte simple) au lieu du StreamField, mais comme nous le verrons par la suite, un StreamField permet de créer des pages bien plus complexes et en utilisant des templates sur mesure pour chaque partie du code, ce que ne permet pas un RichTextField.

Pour que notre code soit utilisable, nous devons créer une migration de la base de donnée, puis appliquer cette migration. Depuis notre dossier app/ :

python manage.py makemigrations home
python manage.py migrate

Pour pouvoir afficher notre page d’accueil sur le site il nous faut un template. Pour chaque classe héritant de la classe Page, le chemin du template pour cette class ese trouve dans app/mon_app/templates/mon_app/maclasse_page.html, maclasse_page.html découlant du nom de notre classe class MaclassePage(Page). Dès lors, pour notre modèle/classe HomePage de l’app home, le template se trouve dans app/home/templates/home/home_page.html. Ce fichier a été automatiquement créé par Wagtail lors de son installation, mais pour les prochaines classes héritant de Page nous devrons créer nous-même les templates.

Ouvrons le fichier app/home/templates/home/home_page.html et remplaçons son contenu par le code suivant :

{% extends "base.html" %}
{% load static wagtailcore_tags wagtailimages_tags %}

{% block body_class %}template-homepage{% endblock %}

{% block content %}
    {% for block in page.body %}
    <div class="row">
        <div class="col">
            {% include_block block %}
        </div>
    </div>
    {% endfor %}
{% endblock content %}

Dans l’interface d’admin de notre site (http://localhost:8000/admin), nous cliquons sur Pages puis sur le crayon à droite de Home.

Pour modifier la page d’accueil de notre site

Nous pouvons dès lors saisir le titre de notre page (qui sera affiché dans la barre de titre du navigateur) et le contenu de notre page. Le fait d’avoir utilisé un StreamField pour le contenu de notre page plutôt qu’un simple RichTextField nous permet de choisir si nous voulons saisir du texte ou une image, puis, en cliquant sur le + qui s’affiche en dessous, nous pouvons compléter notre page avec à nouveau le choix entre du texte ou une image.

Page d’accueil vide prête à être complétée
On ajoute un texte puis une image

Une fois votre page d’accueil complétée, n’oubliez pas de cliquer sur le bouton Save Draft pour sauvegarder vos données. En cliquant ensuite sur le bouton Preview nous pouvons avoir un aperçu de ce que donnera notre page une fois publiée. Si la page vous semble correcte, vous pouvez cliquer sur la petite flèche à droite du bouton Save Draft puis, dans le menu déroulant qui s’affiche, cliquer sur Publish. La page d’accueil de notre site a ainsi été modifiée et nous pouvons la voir en allant sur notre site http://localhost:8000.

Changer la langue de l’interface d’admin

Vous aurez constaté que l’interface d’admin de Wagtail est en anglais. Pourtant une traduction française est disponible. Si vous désirez garder l’anglais par défaut pour le site mais utiliser le français pour l’interface d’admin, vous pouvez cliquer sur la flèche à droite de votre nom d’utilisateur, dans le menu en bas à gauche de l’interface d’admin et cliquer sur Account Settings. Ensuite cliquez sur Language Preferences, sélectionnez French dans le menu déroulant et cliquez sur Update. L’interface d’admin est dorénavant en francais, victoire !

Néanmoins elle n’est en français que pour vous. Si un autre utilisateur se connecte à l’interface d’admin elle sera à nouveau en anglais. Si la majorité de vos utilisateurs est francophone, il est préférable d’effectuer la modification globalement. Pour ce faire nous modifions le fichier app/mon_site/settings/base.py et nous remplaçons la ligne LANGUAGE_CODE = 'en-us' par LANGUAGE_CODE = 'fr-ch' (pour le français de Suisse, sinon fr-fr pour la France, fr-ca pour le Canada, etc.).

#LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'fr-ch'

Ce code change la lange par défaut de votre site, c’est à dire aussi de la partie visible par vos visiteurs. Il faut en tenir compte si votre site est multilingue.

On peut aussi en profiter pour adapter le fuseau horaire avec le code suivant :

#TIME_ZONE = 'UTC'
TIME_ZONE = 'Europe/Zurich'

On sauvegarde le fichier et on rafraîchit l’interface d’admin de notre site. Il est dorénavant en français par défaut pour tous les utilisateurs. Ceux-ci peuvent néanmoins choisir une autre langue en utilisant le cheminement indiqué précédemment.

Si après avoir modifié la langue par défaut de votre site Wagtail vous avez une erreur de type ValidationError avec l’indication {‘locale’: [« L’instance locale avec l’id 1 n’existe pas »]}, vous devez rajouter l’app wagtail.locales dans le fichier app/mon_site/settings/base.py, puis dans l’admin de Wagtail, dans Paramètres -> Régions, sélectionner la langue désirée.

...
    'wagtail.core',
    'wagtail.locales',
...

Intégrer Bootstrap pour améliorer la ligne graphique

Vous l’aurez sans doute remarqué, le graphisme par défaut de notre site est… inexistant ! En effet, Wagtail fournit une interface d’admin très léchée, mais nous laisse le champs vierge pour la partie visiteur.

Pour y remédier, je vous propose d’ajouter Bootstrap pour obtenir rapidement une ligne graphique plus élégante.

Ouvrons le fichier app/mon_site/templates/base.html et ajoutons le code fourni par Bootstrap pour le CSS dans la partie indiquée dans le commentaire {# Global stylesheets #} comme suit :

{# Global stylesheets #}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="{% static 'css/mon_site.css' %}">

Dans le même fichier, dans la section {# Global javascript #} nous ajoutons le code suivant :

{# Global javascript #}
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>
<script type="text/javascript" src="{% static 'js/mon_site.js' %}"></script>

Puis, toujours dans le même fichier, nous entourons le code {% block content %}{% endblock %} d’un <div class="container">...</div> afin de centrer notre contenu dans la page.

<div class="container">
    {% block content %}{% endblock %}
</div>

Finalement, nous ajoutons un entête à notre site avec le code suivant, toujours dans le même fichier, juste après le code <body class="body {% block body_class %}{% endblock %}">{% wagtailuserbar %} :

<nav class="navbar navbar-expand-lg navbar-light bg-light mb-3">
    <div class="container">
        <a class="navbar-brand" href="{% url 'wagtail_serve' '' %}">{% with self.get_site.site_name as site_name %}{% if site_name %}{{ site_name }}{% else %}Mon Site{% endif %}{% endwith %}</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
    </div>
</nav>

Notre fichier doit dorénavant ressembler à cela :

{% load static wagtailuserbar %}

<!DOCTYPE html>
<html class="no-js" lang="en">
    <head>
        <meta charset="utf-8" />
        <title>
            {% block title %}
                {% if self.seo_title %}{{ self.seo_title }}{% else %}{{ self.title }}{% endif %}
            {% endblock %}
            {% block title_suffix %}
                {% with self.get_site.site_name as site_name %}
                    {% if site_name %}- {{ site_name }}{% endif %}
                {% endwith %}
            {% endblock %}
        </title>
        <meta name="description" content="" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />

        {# Global stylesheets #}
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
        <link rel="stylesheet" type="text/css" href="{% static 'css/mon_site.css' %}">

        {% block extra_css %}
            {# Override this in templates to add extra stylesheets #}
        {% endblock %}
    </head>

    <body class="{% block body_class %}{% endblock %}">
        {% wagtailuserbar %}

        <nav class="navbar navbar-expand-lg navbar-light bg-light mb-3">
            <div class="container">
                <a class="navbar-brand" href="{% url 'wagtail_serve' '' %}">{% with self.get_site.site_name as site_name %}{% if site_name %}{{ site_name }}{% else %}Mon Site{% endif %}{% endwith %}</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
            </div>
        </nav>

        <div class="container">
            {% block content %}{% endblock %}
        </div>

        {# Global javascript #}
        <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>
        <script type="text/javascript" src="{% static 'js/mon_site.js' %}"></script>

        {% block extra_js %}
            {# Override this in templates to add extra javascript #}
        {% endblock %}
    </body>
</html>

Avez-vous remarqué le code {% with self.get_site.site_name as site_name %}{% if site_name %}{{ site_name }}{% else %}Mon Site{% endif %}{% endwith %} dans l’entête que nous venons d’ajouter ? Wagtail fourni automatiquement le nom de notre site une fois que nous l’avons indiqué dans l’interface d’admin. Pour ce faire nous devons aller dans Paramètres -> Sites et indiquer le nom de notre site dans le champ… Nom du site.

Changer le nom de notre site

Ajouter l’affichage en colonnes dans notre StreamField

Nous commençons à pouvoir créer une page d’accueil pas trop mal, mais ce serait nettement mieux si nous pouvions décider de placer certains contenus dans des colonnes.

Nous allons créer de nouveaux blocks que nous intégrerons à notre StreamField et qui nous permettrons de définir du contenu sur 2 ou 3 colonnes. Pour cela nous créons un fichier blocks.py dans notre app home et nous y ajoutons le code suivant :

from wagtail.core import blocks
from wagtail.images.blocks import ImageChooserBlock


class ColumnBlock(blocks.StreamBlock):
    paragraph = blocks.RichTextBlock(label='Paragraphe')
    image = ImageChooserBlock()

    class Meta:
        template = 'blocks/column.html'


class TwoColumnBlock(blocks.StructBlock):
    position = blocks.ChoiceBlock(choices=[
        ('left', '70%-30%'),
        ('middle', '50%-50%'),
        ('right', '30%-70%')
    ], label="Disposition des colonnes")
    left_column = ColumnBlock(icon='arrow-left', label='Colonne de gauche')
    right_column = ColumnBlock(icon='arrow-right', label='Colonne de droite')

    class Meta:
        template = 'blocks/two_column_block.html'
        icon = 'placeholder'
        label = 'Deux Colonnes'


class ThreeColumnBlock(blocks.StructBlock):
    left_column = ColumnBlock(icon='arrow-left', label='Colonne de gauche')
    middle_column = ColumnBlock(label='Colonne centrale')
    right_column = ColumnBlock(icon='arrow-right', label='Colonne de droite')

    class Meta:
        template = 'blocks/three_column_block.html'
        icon = 'placeholder'
        label = 'Trois Colonnes'

Puis dans le fichier models.py, toujours dans notre app home, nous intégrons ces nouveaux blocks à notre StreamField. Nous commençons par importer ces classes nouvellement créées :

from .blocks import TwoColumnBlock, ThreeColumnBlock

Puis nous mettons à jour la classe HomePage comme suit :

class HomePage(Page):
    body = StreamField([
        ('paragraph', blocks.RichTextBlock(
            label='Paragraphe', features=['h1', 'h2', 'h3', 'bold', 'italic', 'link', 'hr']
        )),
        ('image', ImageChooserBlock()),
        ('two_columns', TwoColumnBlock()),
        ('three_columns', ThreeColumnBlock()),
    ], verbose_name='Contenu', null=True)

...

Une fois le modèle adapté, nous devons créer une nouvelle migrations de la base de données et l’appliquer avec les commandes suivantes :

python3 manage.py makemigrations home
python3 manage.py migrate

Finalement nous devons créer les templates qui nous permettrons d’appliquer la mise en page désirée.

Nous créons tout d’abord le template qui permet de créer une colonne, nous créons le fichier app/home/templates/blocks/column.html et y ajoutons le code suivant :

{% load wagtailcore_tags wagtailimages_tags %}

{% if blocks %}

    {% for block in blocks %}
        {% if block.block_type == 'image' %}
            <section class="block-{{ block.block_type }} text-center">
                {% image block.value width-420 class="img-responsive" %}
            </section>
        {% else %}
           <section class="block-{{ block.block_type }}">
               {{ block }}
           </section>
        {% endif %}
    {% endfor %}

{% endif %}

Puis nous créons le fichier app/home/templates/blocks/two_column_block.html avec ce code :

{% load wagtailcore_tags %}
<div class="row my-3">

    {% if self.position == 'left' %}
        <div class="col-md-7">
            {% for block in self.left_column %}
                {% include_block block %}
            {% endfor %}
        </div>
        <div class="col-md-5">
            {% for block in self.right_column %}
                {% include_block block %}
            {% endfor %}
        </div>
    {% elif self.position == 'middle' %}
        <div class="col-md-6">
            {% for block in self.left_column %}
                {% include_block block %}
            {% endfor %}
        </div>
        <div class="col-md-6">
            {% for block in self.right_column %}
                {% include_block block %}
            {% endfor %}
        </div>
    {% else %}
        <div class="col-md-5">
            {% for block in self.left_column %}
                {% include_block block %}
            {% endfor %}
        </div>
        <div class="col-md-7">
            {% for block in self.right_column %}
                {% include_block block %}
            {% endfor %}
        </div>
    {% endif %}

</div>

Finalement, nous créons le fichier app/home/templates/blocks/three_column_block.html comme suit :

{% load wagtailcore_tags %}
<div class="row my-3">

    <div class="col-md-4">
        {% for block in self.left_column %}
            {% include_block block %}
        {% endfor %}
    </div>
    <div class="col-md-4">
        {% for block in self.middle_column %}
            {% include_block block %}
        {% endfor %}
    </div>
    <div class="col-md-4">
        {% for block in self.right_column %}
            {% include_block block %}
        {% endfor %}
    </div>

</div>

Nous pouvons dorénavant créer une page d’accueil bien plus complexe.

Page d’accueil avec l’affichage en colonnes

Dans mon prochain article Ajouter des pages classiques et un menu au CMS Wagtail, je vous montre comment ajouter des pages classiques à notre site et y intégrer un menu.

N’hésitez pas à laisser vos commentaires ci-dessous, je me ferai un plaisir de les lire et d’y répondre.

Si vous n’êtes pas sûr de votre code, vous pouvez retrouver tout le code présenté ci-dessus sur le repo Github dédié https://github.com/egrisel/site-wagtail/tree/01-installation.

5 Commentaires

  1. Constance

    Bonjour,

    merci pour ce tuto, je cherche à redimensionner l’image dans les colonnes créées avec blocks.py mais elles apparaissent tjs dans leur taille d’origine, pas adaptées à la largeur des colonnes, est-ce que vous avez une solution ?
    Merci

    Réponse
    • Etienne

      Bonjour Constance,
      Et désolé pour ma réponse tardive.
      As-tu bien ajouté le code suivant dans le fichier app/home/templates/blocks/column.html :
      {% if block.block_type == ‘image’ %}

      {% image block.value width-420 class= »img-responsive » %}

      {% else %}

      {{ block }}

      {% endif %}

      Si ma mémoire est bonne, la classe img-responsive bien avec Bootstrap et permet d’ajuster la taille des images dans les colonnes.
      Etienne

      Réponse
  2. ty cal

    bjr
    et merci pour travail que j’ai pu suivre pas à pas.
    comme je suis novice une question bête.
    quand je voudrais relancer les jours comment dois-je proceder ?
    merci pour votre réponse
    ty

    Réponse
    • Etienne

      Bonjour Ty, et merci pour ton commentaire.
      Je ne suis pas sûr de comprendre ta question. Pourrais-tu préciser ce que tu veux faire ?
      Etienne

      Réponse
  3. SIWA Andy

    Un grand merci M. Etienne pour ce tutoriel non seulement en français mais plutôt bien développé. Un contenu qui se démarque génialement par son caractère pratique et bien illustré.
    Pour un passionné de Django et de python en général comme moi je suis plus à l’aise avec le backend, la logique du site et votre tutoriel m’apporte justement une compétence complémentaire avec du style CSS et une structure pro dans le frontend suivant une méthode. Ce qui, je souligne, est un point en plus par rapport à la documentation officielle de Watgail.
    J’aurais bien des choses à partager après ce parcours, en immersion dans votre blog.

    Réponse

Soumettre un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *