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

272 lines
8.8 KiB
Plaintext
Raw Normal View History

2020-04-13 15:01:36 +02:00
== Templates
2015-12-21 21:43:55 +01:00
Avant de commencer à interagir avec nos données au travers de listes, formulaires et d'interfaces sophistiquées, quelques mots sur les templates: il s'agit 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.
2015-12-21 21:43:55 +01:00
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:
2015-12-21 21:43:55 +01:00
2020-02-17 20:45:39 +01:00
[source,html]
----
<!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` d'un 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 d'accéder aux propriétés de cette objet de la même manière: `{{ wishlist.id }}`, `{{ wishlist.description }}`, ... et d'ainsi respecter la mise en page que nous souhaitons.
2015-12-21 21:43:55 +01:00
En reprenant l'exemple de la page HTML définie ci-dessus, on pourra l'agrémenter de la manière suivante:
2020-02-17 20:45:39 +01:00
[source,django]
----
<!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>
----
2020-04-13 15:01:36 +02:00
image::images/html/my-first-wishlists.png[]
2020-02-17 20:45:39 +01:00
2020-04-13 15:01:36 +02:00
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:
[source,html]
----
<!-- templates/base.html -->
{% load static %}<!doctype html>
2020-04-13 15:01:36 +02:00
<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:
[source,html]
----
<!-- 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
2020-02-17 20:45:39 +01:00
==== Répertoires de découverte des templates
2020-02-17 20:45:39 +01:00
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:
[source,bash]
----
$ tree templates/
templates/
└── wish
└── list.html
----
Par défaut, Django cherchera les templates dans les répertoirer d'installation.
Vous devrez vous éditer le fichier `gwift/settings.py` et ajouter, dans la variable `TEMPLATES`, la clé `DIRS` de la manière suivante:
2020-02-17 20:45:39 +01:00
[source,python]
----
TEMPLATES = [
{
...
'DIRS': [ 'templates' ],
...
},
]
----
==== Fichiers statiques
(à compléter)
2020-04-13 15:01:36 +02:00
=== Builtins
2020-02-17 20:45:39 +01:00
Django vient avec un ensemble de *tags* ou *template tags*. On a vu la boucle `for` ci-dessus, mais il existe https://docs.djangoproject.com/fr/1.9/ref/templates/builtins/[beaucoup d'autres tags nativement présents]. Les principaux sont par exemple:
2020-02-17 20:45:39 +01:00
* `{% if ... %} ... {% elif ... %} ... {% else %} ... {% endif %}`: permet de vérifier une condition et de n'afficher 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 ... %}`.
2020-04-13 15:01:36 +02:00
* `{% 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.
2020-02-17 20:45:39 +01:00
* ...
2020-04-13 15:01:36 +02:00
Chacune de ces fonctions peut être utilisée autant au niveau des templates qu'au niveau du code. Il suffit d'aller les chercher dans le package `django.template.defaultfilters`. Par exemple:
[source,python]
----
from django.db import models
from django.template.defaultfilters import urlize
class Suggestion(models.Model):
2020-04-13 15:01:36 +02:00
"""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*:
2020-04-13 15:01:36 +02:00
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, c'est *via* les tags `{% nom_de_la_fonction param1 param2 ... %}`.
3. *Les tags d'inclusion*: ils retournent un contexte (ie. un dictionnaire), qui est ensuite passé à un nouveau template. Type `{% include '...' ... %}`.
Pour l'implémentation:
2020-02-17 20:45:39 +01:00
1. On prend l'application `wish` et on y ajoute un répertoire `templatetags`, ainsi qu'un fichier `__init__.py`.
2. Dans ce nouveau paquet, on ajoute un nouveau module que l'on 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).
2020-04-13 15:01:36 +02:00
2020-02-17 20:45:39 +01:00
[source,bash]
----
[Inclure un tree du dossier template tags]
----
2020-04-13 15:01:36 +02:00
Pour plus d'informations, la https://docs.djangoproject.com/en/stable/howto/custom-template-tags/#writing-custom-template-tags[documentation officielle est un bon début].
==== Filtres
2020-02-17 20:45:39 +01:00
[source,python]
----
# wish/tools.py
2020-02-17 20:45:39 +01:00
from django import template
2020-02-17 20:45:39 +01:00
from wish.models import Wishlist
2020-02-17 20:45:39 +01:00
register = template.Library()
2020-02-17 20:45:39 +01:00
@register.filter(is_safe=True)
def add_xx(value):
return '%sxx' % value
2020-04-13 15:01:36 +02:00
----
==== Tags simples
[source,python]
----
# wish/tools.py
from django import template
from wish.models import Wishlist
register = template.Library()
2020-02-17 20:45:39 +01:00
@register.simple_tag
def current_time(format_string):
return datetime.datetime.now().strftime(format_string)
2020-04-13 15:01:36 +02:00
----
==== Tags d'inclusion
[source,python]
----
# wish/tools.py
from django import template
from wish.models import Wishlist
register = template.Library()
2020-02-17 20:45:39 +01:00
@register.inclusion_tag('wish/templatetags/wishlists_list.html')
def wishlists_list():
return { 'list': Wishlist.objects.all() }
----
2020-04-13 15:01:36 +02:00
=== Contexts Processors
Un `context processor` permet d'ajouter des informations par défaut à un contexte (le dictionnaire qu'on passe de la vue au template).
L'idée est d'ajouter 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. https://stackoverflow.com/questions/60515797/default-context-for-all-pages-django[StackOverflow] - à retravailler)
[source,python]
----
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(),
}
----
[source,python]
----
'OPTIONS': {
'context_processors': [
....
'core.context_processors.add_variable_to_context',
....
],
},
----