== Templates


=== Structure et configuration

==== Répertoires de découverte des templates

[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:

[source,python]
----
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 https://docs.djangoproject.com/fr/1.9/ref/templates/builtins/[beaucoup d'autres tags nativement présents]. Les principaux sont par exemple:

* `{% 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 ... %}`.
* `{% 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 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):
    """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)

----

==== Regroup By

(le truc super facile qui sert à mort dans plein de cas sans qu'on n'ait à se casser la tête).

=== 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, 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:

 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).

[source,bash]
----
[Inclure un tree du dossier template tags]
----

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

[source,python]
----
# 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

[source,python]
----
# 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 d'inclusion

[source,python]
----
# 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 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',
        ....
    ],
},
----
