Makina Blog
Wagtail : Créer ses modèles de type de contenu (partie 1)
On commence à avoir l'habitude d'initialiser son CMS directement depuis une interface web, cela incluant souvent de nombreux panneaux complexes. Wagtail reprend plutôt la philosophie Django et nous propose de définir les types de contenus comme des modèles.
Une connaissance des principes de base de Django est requise pour la lecture de cette article.
Initialisation de Wagtail
L' initialisation d'un site Wagtail est très similaire à celle d'un site Django : créer un environnement virtuel, installer wagtail, puis utiliser la commande wagtail. Il suffit ensuite de créer la base de données et un compte d'administrateur :
$ mkvirtualenv mon_site_wagtail
$ pip install wagtail
$ wagtail start mon_site_wagtail
$ cd mon_site_wagtail
$ python manage.py migrate
$ python manage.py createsuperuser
Nous avons deux applications créées par défaut: home et search. home contient un type de contenu tandis que search définis une vue permettant de faire des recherches.
Champs
Chaque type de contenu est en fait un modèle Django héritant du modèle Page. Ce modèle possède un certain nombre de champs par défaut (tel qu'un titre, un slug, …). La liste des champs par défaut est présent dans la documentation.
Les champs utilisés sont les mêmes que Django (CharField, DateTimeField, …), en plus des champs RichTextField, permettant l'utilisation d'un WYSIWYG, et StreamField, que nous verrons juste après. Créons notre premier type de contenu en modifiant HomePage :
class HomePage(Page):
sous_titre = models.CharField(max_length=255)
intro = RichTextField()
image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
L'ajout d'une image est une simple ForeignKey vers le modèle wagtailimages.Image de Wagtail. Il est possible de lier un document de la même facon avec le modèle wagtaildocs.Document.
StreamField
Un StreamField est un champ un peu particulier permettant le mélange de plusieurs type de champs de manière organisée. Une présentation est disponible sur YouTube. Voyons comment créer un StreamField :
class HomePage(Page):
[...]
corps = StreamField([
('titre', blocks.CharBlock(classname="full title")),
('paragraphe', blocks.RichTextBlock()),
('image', ImageChooserBlock()),
])
Un StreamField est une liste de blocks. La liste complète des blocks disponibles est dans la documentation officielle.
Bien, maintenant que nous avons définis les champs de notre type de contenu, il ne nous reste plus qu'a choisir où les afficher dans l'écran d'édition.
Panels
Wagtail propose plusieurs panneaux (onglets) pour organiser les champs. content\_panels pour le contenu en lui même, promote\_panels pour les métadonnées et settings\_panels pour les paramètres :
class HomePage(Page):
# [...]
content_panels = Page.content_panels + [
FieldPanel('sous_titre'),
ImageChooserPanel('image'),
FieldPanel('intro', classname="full"),
StreamFieldPanel('corps')
]
Un panneau est une liste de FieldPanels. Il en existe plusieurs types.
FieldPanel
FieldPanel pour tous les champs de base. FieldPanel peut prendre le paramètre classname pour ajouter une classe au champ. Wagtail met plusieurs classes utiles à disposition : - title, indiquant un champ titre. - full, permettant au champs de prendre toute la place à disposition. - col1 à col12, indiquant la taille en nombre de colonnes (sur douze) que le champ prendra.
StreamFieldPanel
StreamFieldPanel pour les StreamField :
ChooserPanel
Les ChooserPanel pour selectionner un objet déjà existant. Il en existe quatre différents : PageChooserPanel, ImageChooserPanel, DocumentChooserPanel ou SnippetChooserPanel :
Organiser les champs
Il existe trois types de FieldPanel permettant de mieux organiser les champs :
MultiFieldPanel
MultiFieldPanel permet de grouper des champs ensemble. Ajouter collapsible en classe permet de créer un accordéon pour ces champs. Ajouter collapsed permet d'avoir l'accordéon fermé par défaut.
content_panels = Page.content_panels + [
MultiFieldPanel([
FieldPanel('sous_titre'),
ImageChooserPanel('image'),
],
heading="Un MultiFieldPanel",
classname="collapsible collapsed",
),
FieldPanel('intro', classname="full"),
StreamFieldPanel('corps')
]
FieldRowPanel
FieldRowPanel permet de réunir plusieurs champs sur une même ligne. Il est important de préciser classname avec la taille des colonnes pour que les champs s'affichent correctement :
content_panels = Page.content_panels + [
FieldRowPanel([
FieldPanel('sous_titre', classname="col6"),
FieldPanel('intro', classname="col6"),
]),
ImageChooserPanel('image'),
StreamFieldPanel('corps')
]
InlinePanel
InlinePanel permet de gérer un autre modèle, et permet notamment de créer un champ multiple.
Les InlinePanels utilisent un modèle spécial, Orderable (Documentation) :
class HomePage(Page):
[...]
content_panels = Page.content_panels + [
[...]
InlinePanel('liens', label="Liens")
]
class HomePageLien(Orderable):
page = ParentalKey(HomePage, related_name='liens')
nom = models.CharField(max_length=255)
url = models.URLField()
panels = [
FieldPanel('nom'),
FieldPanel('url'),
]
ParentalKey est juste une ForeignKey utilisé pour django-modelcluster (pour plus d'information, voir la documentation).
Comme tout modèle Django, nous devons d'abord créer et faire tourner les migrations :
$ python manage.py makemigrations
$ python manage.py migrate
N'hésitez pas à consulter la Documentation officielle pour pousser plus loin.
Nous pouvons dès à présent créer nos premiers contenus. Mais il reste un dernier point à comprendre avant de se jeter la-dessus, et pas des moindres.
Hiérarchie
Wagtail organise les contenus par arborescence (hiérarchie). Comme tout arbre, il existe par défaut une page racine d'un site (oui, Wagtail est multisite par défaut, mais nous reviendrons sur ce point dans un prochain article).
À cette page racine, on y rattache traditionnellement une page enfant de type page d'accueil. C'est le premier niveau. Puis à partir de cette page d'accueil, on aura des pages enfants de type listes (deuxième niveau) et enfin des pages de détails.
Pour reprendre notre exemple, on va considérer que notre page d'accueil va contenir une page de blog qui listera les billets de blog en mode aperçu. Pour cela, il faut que cette page d'index de blog n'accepte que les pages de type blog comme enfant. Inversement, on peut définir dans les pages de blog que seule la page d'index peut être la parente.
Pour y parvenir, Wagtail met à disposition deux attributs parent_page_types
et subpage_types
à définir dans les modèles de pages (Documentation).
Pour notre page d'accueil, nous allons donc créer un autre modèle BlogIndex qui n'acceptera que les pages de type blog en enfant :
class BlogIndex(Page):
[...]
subpage_types = ['app.BlogPage']
Et ajouter un modèle BlogPage qui n'autorisera que la(es) page(s) de type BlogIndex à être parente, et qui ne pourra pas avoir elle-même d'enfant :
class BlogPage(Page):
[...]
parent_page_types = ['app.BlogIndex']
subpage_types = []
Conclusion
Vous savez désormais tout sur les Pages. Ou presque. Nous verrons dans la prochaine partie comment utiliser ce modèle ainsi que son manager.
Formations associées
Formations Django
Formation Django avancé
À distance (FOAD) Du 9 au 13 décembre 2024
Voir la formationActualités en lien
Utiliser des fonctions PostgreSQL dans des contraintes Django
Cet article vous présente comment utiliser les fonctions et les check constraints
PostgreSQL en tant que contrainte sur vos modèles Django.
Wagtail : Comment écrire les templates (partie 3)
Il n'y a pas de vue à proprement parlé dans Wagtail. Tout est en fait géré dans le modèle. Nous pouvons ainsi modifier le template utilisé ou étendre son contexte en fonction du type de contenu sans problème.
Wagtail : Utiliser le modèle Page ainsi que son Manager (partie 2)
Le modèle Page contient plusieurs méthodes spécifiques à l'outil Wagtail. C'est également le cas de son manager. Nous allons faire le tour de tout cela dans cet article.