• Aucun résultat trouvé

Première étape : la fonction de compilation

III. Techniques avancées 122

14. Simplifions nos templates : filtres, tags et contextes 151

14.4. Des structures plus complexes : les custom tags

14.4.1. Première étape : la fonction de compilation

À chaque fois que le parseur de template rencontre un tag, il appelle la méthode correspondant au nom du tag enregistré comme pour nos filtres. La fonction se charge ici de vérifier si les paramètres fournis sont corrects ou de renvoyer une erreur si jamais le tag est mal utilisé. Nous allons nous baser sur un exemple assez simple pour commencer : afficher un nombre aléatoire compris entre deux arguments. Cette opération est notamment impossible avec un filtre, ou du moins pas proprement.

Notre tag pourra être utilisé de la façon suivante : {% random 0 42 %} et renverra donc un nombre entier compris entre 0 et 42. Il faudra faire attention à ce que les paramètres soient bien des entiers, et que le premier soit inférieur au second.

Contrairement au filtre, Django requiert que notre méthode prenne deux arguments précis : parser, qui est l’objet en charge de parser le template actuel (que nous n’utiliserons pas ici), etto ken, qui contient les informations sur le tag actuel, comme les paramètres passés.tokencontient

de plus quelques méthodes sympathiques qui vont nous simplifier le traitement des paramètres.

Par exemple, la méthode split_contents() permet de séparer les arguments dans une liste.

Il est extrêmement déconseillé d’utiliser la méthode classique token.contents.split(' '), qui pourrait « casser » vos arguments si jamais il y a des chaînes de caractères avec des espaces.

Voici un bref exemple de fonction de compilation :

1 from django import template 23 def random(parser, token):

4 """ Tag générant un nombre aléatoire, entre les bornes données en arguments """

5 # Séparation des paramètres contenus dans l'objet token. Le premier

6 # élément du token est toujours le nom du tag en cours

7 try:

8 nom_tag, begin, end = token.split_contents() 9 except ValueError:

10 msg = 'Le tag %s doit prendre exactement deux arguments.' % token.split_contents()[0]

11 raise template.TemplateSyntaxError(msg) 12

13 # Nous vérifions ensuite que nos deux paramètres sont bien des entiers

14 try:

15 begin, end = int(begin), int(end) 16 except ValueError:

17 msg =

'Les arguments du tag %s sont obligatoirement des entiers.'

% nom_tag

18 raise template.TemplateSyntaxError(msg)

1920 # Nous vérifions si le premier est inférieur au second 21 if begin > end:

22 msg =

'L\'argument "begin" doit obligatoirement être inférieur à l\'argument "end" dans le tag %s.'

% nom_tag

23 raise template.TemplateSyntaxError(msg) 2425 return RandomNode(begin, end)

Jusqu’ici, il n’y a qu’une suite de conditions afin de vérifier que les arguments sont bien ceux attendus. Si jamais un tag est mal formé (nombre d’arguments incorrect, types des arguments invalides, etc.), alors le template ne se construira pas etune erreur HTTP 500 sera renvoyée au client, avec comme message d’erreur ce qui est précisé dans la variable msg, si jamais vous êtes en mode « debug » (voir la figure suivante).

Figure 14.3. – Diagramme UML de notre classe RandomNode

Il ne nous reste plus qu’à écrire la classeRandomNode, qui est renvoyée par la méthode ci-dessus.

Vu son appel, il semble évident que sa méthode __init__prend trois arguments :self,begin etend. Comme nous l’avons vu tout à l’heure, cette classe doit également définir une méthode render(self, context), qui va renvoyer une chaîne de caractères, qui remplacera notre tag dans notre rendu HTML. Cette méthode prend en paramètre le contexte du template, auquel nous pouvons accéder et que nous pouvons éditer.

1 from random import randint 2

3 class RandomNode(template.Node):

4 def __init__(self, begin, end):

5 self.begin = begin

6 self.end = end

78 def render(self, context):

9 return str(randint(self.begin, self.end))

Comme pour la fonction de structuration, le code en lui-même n’est pas complexe. Nous nous contentons ici de nous souvenir des arguments, et une fois que la fonction render est appelée, nous générons un nombre aléatoire. Il ne faut cependant pas oublier de le transposer en chaîne de caractères, puisque Django fait après une simple concaténation des nœuds !

Il ne nous reste plus qu’à enregistrer notre tag désormais ! Comme pour les filtres, il existe plusieurs méthodes :

— @register.tag() au début de notre fonction de compilation ;

— @register.tag(name='nom_du_tag') si jamais nous prenons un nom différent ;

— register.tag('nom_du_tag', random) pour l’enregistrer après la déclaration de la fonction.

Ici, nous allons garder la première méthode, comme pour les filtres. Au final, notre tag complet ressemble à ceci :

1 from django import template 2 from random import randint 3

4 register = template.Library() 5

6 @register.tag

7 def random(parser, token):

8

""" Tag générant un nombre aléatoire, entre les bornes données en arguments """

9 try:

10 nom_tag, begin, end = token.split_contents() 11 except ValueError:

12 msg = 'Le tag %s doit prendre exactement deux arguments.' % token.split_contents()[0]

13 raise template.TemplateSyntaxError(msg)

1415 # Nous vérifions que nos deux paramètres sont bien des entiers

16 try:

17 begin, end = int(begin), int(end) 18 except ValueError:

19 msg =

'Les arguments du tag %s sont obligatoirement des entiers.'

% nom_tag

20 raise template.TemplateSyntaxError(msg)

2122 # Nous vérifions si le premier est bien inférieur au second 23 if begin > end:

24 msg =

'L\'argument "begin" doit obligatoirement être inférieur à l\'argument "end" dans le tag %s.'

% nom_tag

Listing 95 – Notre tag random

i

Si vous oubliez d’enregistrer votre tag et que vous tentez tout de même de l’utiliser, vous obtiendrez l’erreur suivante :Invalid block tag: 'random'.

Nous allons enfin pouvoir en profiter dans notre template ! En incorporant{% random 1 20 %}, vous allez afficher un nombre compris entre 1 et 20 à chaque appel de la page. Vous pouvez d’ailleurs tester les cas incorrects cités dans la méthode de compilation. Par exemple, {% ran dom "a" 10 %} affiche la page d’erreur 500 suivante :

Figure 14.4. – Erreur 500 lorsque le tag est mal utilisé