Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Django
  3. Pages et gabarits
Extrait - Django Développez vos applications web en Python (fonctionnalités essentielles et bonnes pratiques)
Extraits du livre
Django Développez vos applications web en Python (fonctionnalités essentielles et bonnes pratiques)
3 avis
Revenir à la page d'achat du livre

Pages et gabarits

Introduction

Le chapitre précédent, relatif aux vues, a montré un travail portant essentiellement sur la donnée entrante ou interne, sur de nombreux aspects : sécurité, contrôle, validité, stockage, actions, etc., jusqu’à le plus souvent rassembler de la donnée sortante. Dans les situations de rendus purement techniques, la production de la sortie est en général totalement prise en charge par le code de la vue, éventuellement avec l’aide d’assistants de mise en forme. L’exemple typique est un ensemble de vues pour servir une API, avec des échanges au format JSON. Un autre usage similaire a été expérimenté avec les vues en mode AJAX, qui rendaient aussi dans ce cas du JSON.

Mais l’usage le plus courant est de vouloir produire des pages au format HTML de façon dynamique, c’est-à-dire que la page est construite à la volée au moment de la demande. Pour autant, de larges parts d’une page sont de nature statique, donc servies sans variation possible, et se retrouvent à l’identique sur plusieurs pages de façon à présenter un site cohérent au niveau parcours et sur le plan graphique. Une infrastructure logicielle digne de ce nom se doit de rationaliser ce genre de composition et ne pas laisser le développeur s’embourber dans...

Moteurs

L’emploi de plus d’un moteur de gabarit au sein d’un projet est supporté. On a quand même plutôt tendance à éviter la dispersion et à rester uniforme en faisant le choix d’un moteur unique.

1. Moteurs intégrés

Historiquement, Django était fourni avec son seul moteur de gabarit maison, dénommé Django Template Language (parfois désigné par le sigle DTL). En version 1.8 est arrivée l’ouverture à l’intégration d’autres moteurs ainsi que le support d’un moteur alternatif : Jinja2 (ou Jinja version 2, en distinction d’une version 1).

Jinja est un moteur de rendu de gabarit, écrit en Python, inspiré de celui de Django, mais issu d’une communauté autre que la Django Software Foundation. Il revendique de donner aux auteurs de gabarits plus de fonctionnalités et d’outils, et d’être plus rapide, notamment par le fait de produire du code Python compilé lors du premier usage, bénéfique aux usages suivants. C’est un moteur pour Python et donc il ne se réduit pas à un usage sous Django.

La popularité de Jinja et ses performances ont fait naître des envies chez certains de le voir supplanter le moteur DTL. Le sujet a été plusieurs fois évoqué, discuté, évalué dans ses pour et contre, et remis en sommeil (pour les raisons récurrentes à ce genre de débat : le manque de ressources humaines pour s’atteler à la réalisation  ; les arbitrages avec d’autres projets).

Les discussions n’ont pas été vaines puisqu’elles ont fait ressortir des objectifs et des points à respecter et ont finalement incité un développeur motivé (et aidé d’un financement participatif) à prendre l’initiative d’une réalisation sur ces propositions d’engagement :

  • Ne pas remplacer ni déprécier DTL (respect de ses partisans...

Principes de fonctionnement

Le système de gabarits a été pensé pour répondre d’abord à des objectifs de présentation, ensuite à une certaine dose de logique dédiée à la présentation, mais pas pour héberger de la logique métier. C’est une volonté manifestée de ne pas permettre d’injecter directement du code métier à partir d’un gabarit, comme certains autres systèmes/langages le permettent avec une alternance continuelle entre du contenu de sortie et des instructions en langage de programmation. Il est estimé que l’écriture d’un gabarit est confiée à un concepteur, pas à un programmeur. Donc il n’est pas prévu de balisage pour basculer temporairement vers du code Python, ni d’inventer un pseudo-langage en imitation.

Par contre, l’utilité est reconnue d’avoir la possibilité de formuler des logiques, mais devant rester très basiques, comme des conditions ou des répétitions, et toujours justifiées par des décisions de présentation. En d’autres termes, tout le travail d’élaboration doit être réalisé dans la couche vue, et la couche gabarit doit se limiter à en exposer le fruit. Le revers de cette rigidité est que parfois on est amené à...

Pages non dynamiques

Tout ce qu’un site délivre n’a pas nécessairement à être servi par des vues. Il existe d’autres moyens mieux adaptés lorsque le contenu est statique. Les sections suivantes mentionnent certains de ces moyens, succinctement car il s’agit juste d’en faire tout de suite la distinction avec la composition dynamique de pages, sujet principal du chapitre.

1. Application flatpages

Le module django.contrib.flatpages est un outil mis à disposition pour répondre à un cas d’usage courant : servir des pages stables, établies d’avance et à lecture seule, telles que « Mentions légales » ou « Conditions d’utilisation ».

Comme son nom l’indique (« page plate » en traduction littérale), les pages concernées doivent rester très simples, avec pour but de délivrer un contenu informatif, sans interaction.

L’application a manifestement des avantages par rapport à l’emploi de simples fichiers statiques :

  • Les pages sont supportées par la base de données. Une page correspond donc à une instance d’un modèle, avec des champs : non seulement un corps HTML, mais aussi un titre HTML. Un autre bénéfice considérable d’avoir ce stockage en base est l’ouverture à une administration de contenu directement par des gestionnaires de contenu, dans un esprit CMS (Content Management System), plutôt que de devoir passer par des administrateurs système ou mettre en jeu une circuiterie sur mesure. Le module d’administration intégré peut suffire à la gestion du cycle de vie de ces pages.

  • L’URL associée à la page peut être choisie à volonté.

  • Le système de gabarit reste mis en œuvre, ce qui assure le respect d’une charte graphique cohérente pour un ensemble de pages, éventuellement différente par rapport au reste du site.

  • Un drapeau permet de restreindre la visibilité de la page aux seuls utilisateurs authentifiés.

Cette application requiert l’installation de l’application django.contrib. sites. Cette dépendance a son explication dans l’ancienneté de ces applications : l’initiateur...

Structuration des pages

La formation d’une page est en grande partie fondée sur de l’assemblage de blocs, avec trois balises structurantes et selon deux grands principes : la surcharge et l’inclusion.

La surcharge consiste à hériter d’un gabarit de plus grande envergure puis à définir ou compléter des contenus pour ses blocs, qu’ils soient vierges ou qu’ils aient déjà un contenu. Une relation d’héritage simple est établie entre un gabarit fils et son éventuel gabarit parent. Le bénéfice typique de cette composition est d’adopter une disposition constante pour un ensemble de pages ou pour le site entier. Les balises de gabarits utilisées sont {% extends %} et {% block %}.

Le sens de raisonnement de la surcharge est orienté du plus fin vers le plus englobant, qu’on peut assimiler à un sens montant. Le chemin y est unique et tout tracé.

L’inclusion complète le tableau en œuvrant dans l’autre sens, le sens descendant, pour faire venir autant de morceaux que voulu et ainsi établir des ramifications. On devine tout de suite les avantages pour des portions devant être affichées sur plus d’une page, à l’identique ou de façon très similaire. La balise à employer est {% include %}.

Expérimentation rapide et manuelle

Il arrive que le fonctionnement ou le résultat d’une balise ou d’un filtre restent obscurs malgré la documentation, ou qu’on tâtonne sur un bon format de conversion. Il est toujours possible d’éditer son gabarit, le sauver, rafraîchir le navigateur, constater le résultat et recommencer. Si le sujet d’expérimentation est limité, le cycle d’essai peut être rendu plus court en faisant des manipulations manuelles en lignes de commande.

Voici un exemple, pour comprendre l’effet de l’échappement automatique, lorsqu’il est actif (par défaut) ou pas :

>>> from django.template import Context, Template 
>>>  
>>> t = Template('{{ v }}' 
... '{% autoescape off %}{{ v }}{% endautoescape %}') 
>>> t.render(Context({'v': ' un <br> deux '})) 
' un &lt;br&gt; deux  un <br> deux ' 

Les commandes peuvent être rapidement retrouvées dans l’historique en naviguant avec les touches fléchées haut et bas, éditées et relancées.

>>> from django.utils.safestring import mark_safe 
>>>  
>>> t.render(Context({'v': mark_safe('<script>danger......

Écritures des gabarits

1. Base du site

Il est courant que le site soit conçu pour présenter ses pages selon la même disposition. Le mieux est bien sûr de n’avoir à écrire cette structure qu’une seule fois et qu’elle soit appliquée à toutes les pages. Ce squelette servira de parent à toutes les pages.

Imaginons une disposition à deux colonnes : à gauche un menu de navigation et à droite un espace de contenu à remplir par chacune des pages. La place de ce gabarit est parmi le répertoire global des gabarits du site.

 Créez le gabarit de base :

mysite\templates\base.html 

{% load static %} 
<!DOCTYPE html> 
<html> 
<head> 
 <title>Site - {% block title %}{% endblock %}</title> 
 <link rel="stylesheet" href="{% static 'css/site.css' %}"> 
</head> 
<body> 
 <div style="float:left; margin-right:20px; width:20%;"> 
  {# à compléter #}ICI UN MENU 
 </div> 
 <div style="float:left;"> 
{% block content %}{% endblock content %} 
 </div> 
</body> 
</html> 

Cette première étape d’écriture montre trois éléments standards de balisage :

  • {% load static %}

Cette balise a été mentionnée auparavant dans des exemples, mais sans être expliquée en détail. Elle demande le chargement de balises et filtres non intégrés et donc fournis par une application installée.

L’emplacement conventionnel de ces composants est sous un module nommé templatetags sous la racine de l’application. Les composants peuvent être rassemblés dans un ou plusieurs fichiers Python de ce répertoire. La répartition est libre, mais devrait être représentative de familles cohérentes. Le nom du fichier est lui aussi libre, mais doit être choisi en tenant compte d’un potentiel risque de conflit de nommage : il s’agit du paramètre à donner à la balise de chargement, sans plus de précision d’origine....

Composants de gabarit personnalisés

Une collection intégrée de balises et de filtres pour gabarit est disponible pour couvrir les besoins les plus courants. Une infrastructure logicielle est également proposée pour étendre ce jeu de base et écrire soi-même des composants taillés à son besoin.

L’écriture d’un composant ne s’improvise pas et obéit à des règles bien précises. Pour les respecter, il suffit de suivre deux conseils simples :

  • Lire la documentation et l’appliquer.

  • S’inspirer des composants existants, lisibles en :

django/templates/defaultfilters.py 
django/templates/defaulttags.py 

Le sujet va être exploré avec l’implémentation d’un exemplaire de chacune de ces catégories de composants.

1. Balise de gabarit

L’objectif est l’écriture d’une balise de gabarit qui rend le nombre de messages non lus du dossier d’arrivée. Il est choisi de présenter cette pièce d’information dans le menu de l’application, au niveau du lien vers le dossier d’arrivée, là où sa compréhension devrait être implicite.

 Complétez le menu :

mysite\templates\base.html 

{% load static %} 
{% load messenger_tags %} 
<!DOCTYPE html> 
[...] 
{% if user.is_authenticated %}<ul>{% messenger_unread as cnt %} 
 <li><a href="{% url 'messenger:inbox' %}">Reçu 
{% if cnt %} <strong>({{ cnt }})</strong>{% endif %} 
 </a></li> 
[...] 

Tout d’abord, la bibliothèque de composants personnalisés doit être chargée. L’opération est demandée avec une balise {% load %}. Il n’y a pas d’impératif technique sur l’endroit dans le fichier où placer ce type de balise, à part bien sûr d’être placé avant toute référence à un de ses composants. Dans la situation actuelle, on pouvait la mettre juste avant l’emploi de la balise personnalisée. Attention toutefois à assurer une cohérence entre le lieu de chargement et tous...