gwift-book/source/part-4-services-oriented-ap.../templates.adoc

8.8 KiB
Raw Blame History

Templates

Avant de commencer à interagir avec nos données au travers de listes, formulaires et dinterfaces sophistiquées, quelques mots sur les templates: il sagit en fait de squelettes de présentation, recevant en entrée un dictionnaire contenant des clés-valeurs et ayant pour but de les afficher selon le format que vous définirez.

En intégrant un ensemble de tags, cela vous permettra de greffer les données reçues en entrée dans un patron prédéfini.

Note
(je ne sais plus ce que je voulais dire ici)

Un squelette de page HTML basique ressemble à ceci:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <title></title>
    </head>
    <body>
        <p>Hello world!</p>
    </body>
</html>

Notre première vue permettra de récupérer la liste des objets de type Wishlist que nous avons définis dans le fichier wish/models.py. Supposez que cette liste soit accessible via la clé wishlists dun dictionnaire passé au template. Elle devient dès lors accessible grâce aux tags {% for wishlist in wishlists %}. A chaque tour de boucle, on pourra directement accéder à la variable {{ wishlist }}. De même, il sera possible daccéder aux propriétés de cette objet de la même manière: {{ wishlist.id }}, {{ wishlist.description }}, …​ et dainsi respecter la mise en page que nous souhaitons.

En reprenant lexemple de la page HTML définie ci-dessus, on pourra lagrémenter de la manière suivante:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <title></title>
    </head>
    <body>
        <p>Mes listes de souhaits</p>
        <ul>
        {% for wishlist in wishlists %}
            <li>{{ wishlist.name }}: {{ wishlist.description }}</li>
        {% endfor %}
        </ul>
    </body>
</html>
my first wishlists

Mais plutôt que de réécrire à chaque fois le même entête, on peut se simplifier la vie en implémentant un héritage au niveau des templates. Pour cela, il suffit de définir des blocs de contenu, et d'étendre une page de base, puis de surcharger ces mêmes blocs.

Par exemple, si on repart de notre page de base ci-dessus, on va y définir deux blocs réutilisables:

<!-- templates/base.html -->
{% load static %}<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <title>{% block title %}Gwift{% endblock %}</title> (1)
    </head>
    <body>
        {% block body %}<p>Hello world!</p>{% endblock %} (2)
    </body>
</html>
  1. Un bloc title

  2. Un bloc body

La page HTML pour nos listes de souhaits devient alors:

<!-- templates/wishlist/wishlist_list.html -->

{% extends "base.html" %} (1)

{% block title %}{{ block.super }} - Listes de souhaits{% endblock %} (2)

{% block body %} (3)
<p>Mes listes de souhaits</p>
<ul>
{% for wishlist in wishlists %}
    <li>{{ wishlist.name }}: {{ wishlist.description }}</li>
{% endfor %}
</ul>
  1. On étend/hérite de notre page base.html

  2. On redéfinit le titre (mais on réutilise le titre initial en appelant block.super)

  3. On définit uniquement le contenu, qui sera placé dans le bloc body.

Structure et configuration

Répertoires de découverte des templates

Il est conseillé que les templates respectent la structure de vos différentes applications, mais dans un répertoire à part. Par convention, nous les placerons dans un répertoire templates. La hiérarchie des fichiers devient alors celle-ci:

$ tree templates/
templates/
└── wish
    └── list.html

Par défaut, Django cherchera les templates dans les répertoirer dinstallation. Vous devrez vous éditer le fichier gwift/settings.py et ajouter, dans la variable TEMPLATES, la clé DIRS de la manière suivante:

TEMPLATES = [
    {
        ...
        'DIRS': [ 'templates' ],
        ...
    },
]

Fichiers statiques

(à compléter)

Builtins

Django vient avec un ensemble de tags ou template tags. On a vu la boucle for ci-dessus, mais il existe beaucoup dautres tags nativement présents. Les principaux sont par exemple:

  • {% if …​ %} …​ {% elif …​ %} …​ {% else %} …​ {% endif %}: permet de vérifier une condition et de nafficher le contenu du bloc que si la condition est vérifiée.

  • Opérateurs de comparaisons: <, >, ==, in, not in.

  • Regroupements avec le tag {% regroup …​ by …​ as …​ %}.

  • {% url %} pour construire facilement une URL à partir de son nom

  • urlize qui permet de remplacer des URLs trouvées dans un champ de type CharField ou TextField par un lien cliquable.

  • …​

Chacune de ces fonctions peut être utilisée autant au niveau des templates quau niveau du code. Il suffit daller les chercher dans le package django.template.defaultfilters. Par exemple:

from django.db import models
from django.template.defaultfilters import urlize


class Suggestion(models.Model):
    """Représentation des suggestions.
    """
    subject = models.TextField(verbose_name="Sujet")

    def urlized_subject(self):
        """
        Voir https://docs.djangoproject.com/fr/3.0/howto/custom-template-tags/
        """
        return urlize(self.subject, autoescape=True)

Non-builtins

En plus des quelques tags survolés ci-dessus, il est également possible de construire ses propres tags. La structure est un peu bizarre, car elle consiste à ajouter un paquet dans une de vos applications, à y définir un nouveau module et à y définir un ensemble de fonctions. Chacune de ces fonctions correspondra à un tag appelable depuis vos templates.

Il existe trois types de tags non-builtins:

  1. Les filtres - on peut les appeler grâce au pipe | directement après une valeur dans le template.

  2. Les tags simples - ils peuvent prendre une valeur ou plusieurs en paramètre et retourne une nouvelle valeur. Pour les appeler, cest via les tags {% nom_de_la_fonction param1 param2 …​ %}.

  3. Les tags dinclusion: ils retournent un contexte (ie. un dictionnaire), qui est ensuite passé à un nouveau template. Type {% include '…​' …​ %}.

Pour limplémentation:

  1. On prend lapplication wish et on y ajoute un répertoire templatetags, ainsi quun fichier init.py.

  2. Dans ce nouveau paquet, on ajoute un nouveau module que lon va appeler tools.py

  3. Dans ce module, pour avoir un aperçu des possibilités, on va définir trois fonctions (une pour chaque type de tags possible).

[Inclure un tree du dossier template tags]

Pour plus dinformations, la documentation officielle est un bon début.

Filtres

# wish/tools.py

from django import template

from wish.models import Wishlist

register = template.Library()

@register.filter(is_safe=True)
def add_xx(value):
    return '%sxx' % value

Tags simples

# wish/tools.py

from django import template

from wish.models import Wishlist


register = template.Library()


@register.simple_tag
def current_time(format_string):
    return datetime.datetime.now().strftime(format_string)

Tags dinclusion

# wish/tools.py

from django import template

from wish.models import Wishlist


register = template.Library()


@register.inclusion_tag('wish/templatetags/wishlists_list.html')
def wishlists_list():
    return { 'list': Wishlist.objects.all() }

Contexts Processors

Un context processor permet dajouter des informations par défaut à un contexte (le dictionnaire quon passe de la vue au template). Lidée est dajouter une fonction à un module Python à notre projet, puis de le référencer parmi les CONTEXT_PROCESSORS de nos paramètres généraux. Cette fonction doit peupler un dictionnaire, et les clés de ce dictionnaire seront directement ajoutées à tout autre dictionnaire/contexte passé à une vue. Par exemple:

(cf. StackOverflow - à retravailler)

from product.models import SubCategory, Category


def add_variable_to_context(request):
    return {
        'subCategories': SubCategory.objects.order_by('id').all(),
        'categories': Category.objects.order_by("id").all(),
    }
'OPTIONS': {
    'context_processors': [
        ....
        'core.context_processors.add_variable_to_context',
        ....
    ],
},