Chapitre 3 : Les bases des pages web dynamiques
Dans le chapitre précédent, nous avons expliqués comment mettre en place un projet Django et démarrer son serveur de développement. Bien sûr, ce site ne fait à vrai dire pas grand chose d’utile pour l’instant — il ne fait qu’afficher le message « It worked! » (ça à marché !). Changeons cela. Ce chapitre vous introduit à la création de pages web dynamiques à l’aide de Django.
Votre première Vue : contenu dynamique
Pour commencer, créons une page web qui affiche l’heure et la date courante. C’est un bon exemple de page web dynamique, car le contenu de la page n’est pas statique — le contenu change en fonction du résultat d’un calcul (dans notre cas, le calcul de la date courante). Cet exemple simple n’implique pas de base de données ou une saisie quelconque par l’utilisateur — uniquement l’affichage de l’horloge interne au serveur.
Pour créer cette page, nous allons écrire une fonction view (ndt : une vue en français). Une fonction view, ou vue pour faire court, est simplement une fonction Python qui prends en charge une requête internet et retourne une réponse. Cette réponse peut être le contenu d’une page web au format HTML, ou une redirection, ou une erreur 404, ou un document XML, ou une image — ou n’importe quoi, vraiment. La vue en elle même contient toute la logique arbitraire qui est nécessaire à la fourniture de cette réponse. Ce code peut se situer où vous voulez, dès l’instant qu’il appartient au chemin d’accès Python. Il n’y a pas d’autre prérequis — pas de « magie », autrement dit. Puisqu’il faut bien mettre le code quelque part, nous allons créer un fichier nommé views.py dans le répertoire monsite, que vous avez crée au chapitre précédent.
Voici une vue qui retourne la date et l’heure courante, sous forme de document HTML :
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
Explorons ce code ligne par ligne :
Tout d’abord, nous importons la classe HttpResponse, qui appartient au module django.http. Consultez l’ annexe H pour obtenir plus de détails sur les objets HttpRequest et HttpResponse.
Ensuite, nous importons le module datetime depuis la bibliothèque standard Python, un jeu de modules très utiles fourni en standard avec Python. Le module datetime contient diverses fonctions et classes qui permettent de gérer les dates et les heures, y compris une fonction qui retourne l’heure courante.
Puis nous définissons une fonction nommée current_datetime. C’est la fameuse fonction view. Chaque vue prends un objet HttpRequest pour premier paramètre, typiquement appelé request (ndt : requête).
Remarquez que le nom de la fonction view importe peu; elle n’a pas à être appelée d’une certaine façon pour être reconnue par Django. Nous l’appellons ici current_datetime car ce nom indique clairement ce qu’elle fait, mais nous aurions tout aussi bien pu l’appeler stupéfiante_super_bluffante_current_time, ou quelque chose d’aussi révoltant, Django s’en moque. La section suivante explique comment Django trouve cette fonction.
La première ligne de code à l’intérieur de la fonction calcule la date et l’heure courante, sous forme d’objet datetime.datetime, et le stock dans la variable locale now.
La seconde ligne de code à l’intérieur de la fonction construit une réponse HTML en utilisant les fonctionnalités de formatage des chaînes de caractères propre à Python. Le %s est un caractère de substitution, et le symbole pourcent situé après la chaîne de caractère signifie « remplace le %s par la valeur de la variable now ». (oui, le HTML n’est pas valide, mais nous essayons de garder l’exemple simple et concis).
Finalement, la vue retourne un objet HttpResponse contenant la réponse générée. Chaque vue à la responsabilité de retourner un objet HttpResponse`. (Il y a des exceptions, mais nous verrons cela plus tard).
Fuseau horaire sous Django
Django embarque un paramétrage du fuseau horaire, TIME_ZONE, configuré par défaut à America/Chicago. Ce n’est probablement pas là que vous vivez, vous voudrez donc le modifier dans votre fichier settings.py. Consultez l’annexe E pour plus de détails.
Correspondance entre Vues et URLs
Pour récapituler, cette fonction view retourne une page HTML incluant la date et l’heure courante. Mais comment précisons nous à Django qu’il faut utiliser ce code ? C’est là que URLconfs entre en scène.
Un URLconf est une sorte de table des matières pour votre site fonctionnant sous Django. Basiquement, c’est une mise en correspondance entre les patrons d’URL et les fonctions qui doivent être appellées pour ces patrons d’URL. C’est la façon dont vous dite à Django, «pour cet URL, appel ce code ci, et pour cet URL, appel ce code là ». Souvenez vous que les fonctions view ont besoin d’être référencées dans votre chemin d’accès Python.
Votre Path Python
Votre path Python (ndt : le fameux « chemin d’accès ») est la liste des répertoires de votre système où Python cherche lorsque vous utilisez la déclaration Python import.
Par exemple, admettons que votre chemin d’accès Python soit configuré ainsi : ['', '/usr/lib/python2.4/site-packages', '/home/username/djcode/']. Si vous exécutez le code Python from foo import bar, Python commencera par rechercher un module appelé foo.py dans le répertoire courant(la première entrée dans le path Python, une chaîne de caractère vide, signifie « le répertoire courant »). Si ce fichier n’existe pas, Python ira chercher le fichier /usr/lib/python2.4/site-packages/foo.py. Si ce fichier n’existe pas, il essaiera /home/username/djcode/foo.py. Enfin, si ce fichier n’existe pas, il lévera une erreur de type ImportError.
Si vous désirez voir la valeur de votre chemin d’accès Python, lancez la console interactive Python et tapez import sys, suivit de print sys.path.
En général, vous n’avez pas à vous soucier du paramétrage de votre « Python path » — Python et Django prendront soins de ces choses pour vous automatiquement, en arrière plan. (Si vous êtes curieux, configurez le chemin d’accès Python est l’une des choses que prend en charge le fichier manage.py).
Lorsque vous avez lancé django-admin.py startproject au chapitre précédent, le script a crée un URLconf pour vous automatiquement : le fichier urls.py. Éditons ce fichier. Par défaut, il ressemble à quelque chose comme ceci :
from django.conf.urls.defaults import *
urlpatterns = patterns('',
# Example:
# (r'^mysite/', include('mysite.apps.foo.urls.foo')),
# Uncomment this for admin:
# (r'^admin/', include('django.contrib.admin.urls')),
)
Explorons ce code ligne par ligne :
- La première ligne importe tous les objets du module django.conf.urls.defaults, y compris une fonction appelée patterns.
- La seconde ligne appelle la fonction patterns() et enregistre le résultat dans la variable appelée urlpatterns. La fonction patterns() reçoit un seul argument — la chaîne vide. Le reste des lignes est commenté (la chaîne peut être utilisée pour fournir un préfixe commun aux fonctions view, mais nous négligerons cette fonctionnalité avancée pour l’instant).
La principale chose à noter ici est la variable urlpatterns, que Django s’attend à trouver dans votre module ROOT_URLCONF. Cette variable définie la correspondance entre les URLs et le code qui les prends en charge.
Par défaut, tout est commenté dans un URLconf — votre application Django est « en blanc » (en apparté, c’est comme cela que Django sait qu’il faut vous montrer la page « It worked! » du précédent chapitre. Si votre URLconf est vierge, Django part du principe que vous venez de commencer un nouveau projet et, ainsi, affiche ce message).
Éditons ce fichier pour afficher notre vue current_datetime :
from django.conf.urls.defaults import *
from monsite.views import current_datetime
urlpatterns = patterns('',
(r'^time/$', current_datetime),
)
Ici, nous avons effectués deux changements. Tout d’abord, nous avons importés la vue current_datetime depuis son module (monsite/views.py, que nous convertissons en monsite.views dans la syntaxe d’import Python). Ensuite, nous avons ajoutés la ligne (r'^time/$', current_datetime),. Cette ligne est un patron d’URL — C’est un tuple Python dans lequel le premier élément est une simple expression régulière et le second élément est la fonction view à utiliser avec ce patron.
En résumé, nous avons simplement indiqués à Django que toute requête vers l’URL /time/ doit être prise en charge par la fonction view appelée current_datetime.
Quelques points méritent précisions :
Notez que dans cet exemple, nous passons la fonction current_datetime en tant qu’objet, sans faire d’appel à la fonction. C’est une fonctionnalité clef de Python (et de bien d’autre langages dynamiques) : les fonctions sont des objets de première classe, ce qui signifie que vous pouvez les transmettre de la même façon que de simples variables.
Le r de r'^time/$' signifie que '^time/$ est une chaîne Python brute. Ceci permet d’écrire les expressions régulières sans caractères d’échappement.
Vous devez enlever la barre oblige située au début de l’expression ‘^time/$’` de façon à obtenir la correspondance avec /time/. Django ajoute automatiquement une barre oblique au début de chaque expression. À première vue, ceci peux paraître curieux, mais les URLconfs peuvent êtres imbriqués dans d’autres URLconfs, aussi retirer la première barre oblique simplifie les choses. Ceci est détaillé au chapitre 8.
L’accent circonflexe (^) et le charactère dollar ($) sont importants. L’accent circonflexe signifie « il faut que le patron corresponde au début de la chaîne », et le symbole dollar signifie « il faut que le patron corresponde à la fin de la chaîne ».
Ce concept s’explique mieux par l’exemple. Si nous avions utilisé à la place le patron '^time/' (sans le symbole dollar à la fin), alors n’importe quel URL débutant par time/ aurait correspondu, comme par exemple /time/foo et /time/bar, et pas seulement /time/. De la même façon, si nous avions omis l’accent circonflexe du début de chaîne, ('time/$'), Django aurait pris en compte tous les URLs se terminant par time/, tel que /foo/bar/time/. Nous utilisons donc les deux symboles, accent circonflexe et dollar, pour s’assurer que seul l’URL /time/ corresponde. Rien de plus, rien de moins.
Vous vous demandez peut-être ce qui arrive si quelqu’un demande /time. Cela sera pris en charge de la façon dont vous pourriez l’espérer (via une redirection), tant que la configuration de APPEND_SLASH est à True (Voir l’annexe E pour une bonne lecture de chevet sur le sujet).
Pour tester nos modifications apportées à l’URLconf, démarrez le serveur de développement de Django comme vous l’avez fait au chapitre 2, en lançant la commande python manage.py runserver. (Si vous l’aviez laissé tourner, c’est bon aussi. Le serveur de développement détecte automatiquement les changements de votre code Python et se met à jour si nécessaire, vous n’avez donc pas à redémarrer le serveur entre les modifications). Le serveur étant accessible à l’adresse http://127.0.0.1:8000, ouvrez un navigateur internet et visitez l’adresse http://127.0.0.1:8000/time/. Vous devriez voir apparaître le rendu de votre vue Django.
Hourra ! Vous avez fait votre première page internet propulsée par Django.
Expressions régulières
Les expressions régulières (ou encore regexes) sont une façon compacte de préciser des patrons dans du texte. Bien que l’URLconf de Django autorise toutes sortes de regexes afin d’offrir de puissantes possibilités dans la correspondance d’URL, vous n’utliserez probablement que quelques patrons d’expressions régulières dans la pratique. Voici une petite sélection de patrons courants :
| Symboles | Correspondances |
|---|---|
| . (point) | N’importe quel caractère |
| \d | N’importe quel chiffre |
| [A-Z] | N’importe quel caractère de l’intervalle A-Z (capitale) |
| [a-z] | N’importe quel caractère de l’intervalle a-z (bas de casse) |
| [A-Za-z] | N’importe quel caractère de l’intervalle a-z (sans sensibilité à la casse) |
| + | Une occurence ou plus de l’expression précedente (par exemple, \d+ retourne un ou plusieurs chiffres) |
| [^/]+ | N’importe quel caractère à l’exception de la barre oblique |
| ? | Zéro ou plusieurs occurences de l’expression précédente (par exemple, \d* renvoie aucun ou plusieurs chiffres) |
| {1,3} | Entre une et trois (inclus) occurences de l’expression précédente |
Pour en savoir plus au sujet des expressions régulières, consultez http://www.djangoproject.com/r/python/re-module/.
Comment Django traite une requête
Il convient de préciser plusieurs choses à propos de ce qui vient de se passer. Voici les explications sur ce qui arrive lorsque vous lancez le serveur de développement de Django et que vous demandez des pages web :
La commande python manage.py runserver importe un fichier appelé settings.py depuis le même répertoire. Ce fichier contient toutes sortes de configurations optionnelles pour cette instance particulière de Django, dont l’une des plus importantes est ROOT_URLCONF. Le paramètre ROOT_URLCONF indique à Django quel est le module Python à utiliser par défaut en guise d’URLconf du site.
Vous vous souvenez lorsque django-admin.py startproject à créé les fichiers settings.py et urls.py ? Et bien le settings.py autogénéré possède un ROOT_URLCONF qui pointe vers le tout autant autogénéré urls.py. Pratique.
Lorsque une requête arrive — admettons une requête vers l’URL /time/ — Django charge l’URLconf indiqué par le paramètre ROOT_URLCONF. Il vérifie ensuite chacun des patrons d’URL de cet URLconf, dans l’ordre, comparant l’URL demandé avec les patrons un à un, jusqu’à ce qu’il en trouve un qui corresponde à ce patron. Lorsqu’il trouve une correspondance, il appelle la fonction view associée à ce patron, lui passant un objet HttpRequest en premier paramètre. (HttpRequest sera détaillé plus tard).
La fonction view à la responsabilité de retourner un object HttpResponse.
Vous avez à présent les bases pour faire des pages à partir de Django. C’est assez simple, vraiment — il suffit d’écrire les vues et de les mettre en relation avec des URLs via les URLconfs. Vous devez vous dire qu’assurer la correspondance entre des URLs et des fonctions en utilisant des expressions régulières doit être lent. Vous serez agréablement surpris.
Comment Django traite une requête : détails complets
En complément de la simplissime correspondance entre URL et Vue que nous venons de décrire, Django fournit un peu de flexibilité dans le traitement des requêtes. Le fonctionnement typique — Une résolution d’URLconf vers une fonction view, qui renvoie une HttpResponse — peut être court-circuité ou enrichi grâce au middleware. Les entrailles secrètes du middleware seront pleinement détaillées au chapitre 15, mais un rapide schéma (voir illustration 3-1) devrait vous aider à assembler les pièces conceptuelles.
Figure 3-1 : Parcours complet d’une requête et d’une réponse sous Django.
Lorsqu’une requête HTTP parvient au navigateur, un handler (ndt : gestionnaire) spécifique au serveur construit l’objet HttpRequest transmis aux composants suivants et gère le flux de traitement de la réponse.
Le « handler » appelle ensuite n’importe quel middleware Request ou View disponible. Ces types de middleware sont utiles pour enrichir les objets HttpRequest entrants et pour fournir des traitements spécifiques à certain types de requêtes. Si aucun ne retourne de HttpResponse, la procédure met de côté la vue.
Les bugs se glissant même chez les meilleurs programmeurs, le middleware exception aide à s’en débarrasser. Si une fonction view lève une exception, le contrôle est pris en charge par le middleware Exception. Si ce middleware ne retourne pas de HttpResponse, l’exception est à nouveau lévée.
Même dans ce cas, tout n’est pas perdu. Django propose des fonctions view par défaut qui créent de sympathiques réponses 404 et 500. Au final, le response middleware est bon pour post-traiter une HttpResponse juste avant son envoi au navigateur, ou pour faire le ménage dans les demandes de ressources spécifiques.
URLconfs et faible couplage
Il est temps à présent de mettre en lumière une philosophie majeure sur laquelle repose les URLconfs et Django en général : le principe de faible couplage. Dit simplement, le faible couplage est une approche du développement logiciel qui considère l’importance de faire des composants interchangeables. Si deux morceaux de code sont faiblement couplés, alors les changements fait à l’un des morceaux n’auront que peu, voir pas, d’effet sur l’autre.
Les URLconfs de Django sont un bon exemple de ce principe en fonctionnement. Dans une application web sous Django, les définitions d’URL et les fonctions view qu’elles appellent sont faiblement couplées; Ceci étant, la décision de savoir comment un URL doit être pour correspondre à une fonction donnée, ainsi que l’implémentation de la fonction elle même, reposent en deux endroits différents. Ceci laisse le développeur substituer un composant sans affecter l’autre.
À l’inverse, les autres plate-formes de développement web couplent les URLs au programme. Dans les applications PHP (http://www.php.net/) typiques, par exemple, l’URL de votre application est déterminé par l’endroit où vous placez le code dans votre système de fichier. Dans les premières versions du framework web Python, CherryPy (http://www.cherrypy.org/), l’URL de votre application correspondait au nom de la méthode dans laquelle le code résidait. Ceci peut sembler être un raccourci pratique à court terme, mais peut devenir ingérable sur le long terme.
Considérons par exemple la fonction view que nous avons écrite auparavant, et qui affiche la date et l’heure courante. Si nous voulons changer l’URL de l’application — par exemple, modifier /time/ en /currenttime/ — nous pouvons faire un rapide changement de l’URLconf, sans avoir à se soucier de l’implémentation sous-jacente de la fonction. De la même façon, si nous désirons changer la fonction view — en altérant quelque peu sa logique — nous pouvons le faire sans affecter l’URL à laquelle la fonction est liée. En outre, si nous voulons rendre accessible le fonctionnalité « date courante » à plusieurs URLs, nous pouvons aisément le faire en éditant l’URLconf, sans avoir à toucher au code de la vue.
Voici donc le faible couplage en action. Nous allons continuer à signaler les exemples de cette importante philosophie tout au long de ce livre.
Erreurs 404
Nous avons jusqu’ici défini, dans notre URLconf, un seul patron d’URL : celui qui prend en charge la requête vers l’URL /time/. Qu’arrive-t-il lorsqu’un URL différent est demandé ?
Pour le savoir, essayez de lancer le serveur Django de développement et de visiter une page du genre http://127.0.0.1:8000/hello/ ou http://127.0.0.1:8000/n-existe-pas/, ou même http://127.0.0.1:8000/ (la racine du site). Vous devriez voir une page portant le message « Page not found » (voir illustration 3-2). (Jolie, n’est-ce pas ? Soyez certain qu’ici, nous aimons nos couleurs pastels). Django affiche ce message parceque vous avez demandé un URL qui n’est pas défini dans votre URLconf.
Figure 3-2. La page 404 de Django.
L’utilité de cette page va bien au delà du basique message d’erreur 404; Elle vous indique aussi précisément quel URLconf Django à utiliser ainsi que tous les patrons de cet URLconf. À partir de ces informations, vous devriez pouvoir dire pourquoi l’URL requis retourne une erreur 404.
Naturellement, ces informations sensibles ne sont destinées qu’à vous, développeur web. Si cela était un site de production déployé en direct sur l’internet, nous ne voudrions pas exposer ces informations au public. Pour cette raison, cette page « Page not found » n’est affichée que si votre projet Django est en mode debug. Nous expliquerons comment désactiver le mode debug plus tard. Pour l’instant, sachez simplement que chaque projet Django est en mode debug lors de sa création, et que si le projet n’est pas en mode debug, une réponse différente est affichée.
Votre seconde vue : URLs dynamiques
Dans notre premier exemple de vue, le contenu de la page « date/heure courante » était dynamique, mais l’URL (/time/) était statique. Dans la plupart des applications web dynamiques, cependant, un URL contient des paramètres qui influencent le rendu de la page.
Créons à présent une seconde vue qui affiche la date et l’heure courante augmentée d’un certain nombre d’heures. Le but est de construire un site de façon à ce que la page /time/plus/1/ affiche l’heure et la date une heure dans le futur, la page /time/plus/2/ affiche l’heure et la date deux heures dans le futur, la page /time/plus/3/ affiche l’heure et la date trois heures dans le futur, et ainsi de suite.
Un débutant pourrait penser à coder une vue séparée pour chaque modèle, ce qui pourrait donner l’URLconf résultant du genre :
urlpatterns = patterns('',
(r'^time/$', current_datetime),
(r'^time/plus/1/$', one_hour_ahead),
(r'^time/plus/2/$', two_hours_ahead),
(r'^time/plus/3/$', three_hours_ahead),
(r'^time/plus/4//$', four_hours_ahead),
)
Cette façon de faire est clairement mauvaise. Non seulement ceci abouti à des fonctions view redondantes, mais limite aussi fondamentalement l’application à ne prendre en charge que les plages horaires prédéfinies — une, deux, trois ou quatre heures. Si, tout à coup, nous voulons créer une page qui affiche l’horaire cinq heures plus tard, nous devrons créer une vue séparée et une ligne dans l’URLconf pour cela, favorisant ainsi la duplication et la folie. Nous devons faire ici quelque abstraction.
Un mot sur les jolis URLs
Si vous avez l’expérience d’une autre plate-forme, comme PHP ou Java, vous pouvez penser, «Hey, utilisons une requête paramétrée ! », quelque chose du genre /time/plus?hours=3, où les heures seraient désignées par le paramètre hours dans la requête de l’URL (la partie après le ?).
Vous pouvez faire cela avec Django (et nous vous dirons comment faire plus tard, si vous deviez vraiment le savoir), mais l’une des philosophies fondatrice de Django est que les URLS doivent être jolis. L’URL /time/plus/3/ est largement plus propre, simple, plus lisible, plus facile à transmettre à quelqu’un de vive voix et … tout simplement plus belle que sa requête paramétrée homologue. Les jolis URLs sont un signe de la qualité d’une application web.
Le système d’URLconf de django encourage les jolis URLs, faisant en sorte qu’il soit plus facile de les utiliser que de ne pas le faire.
Patron d’URL générique
Continuons avec notre exemple hours_ahead, en ajoutant un joker dans le patron d’URL. Comme nous l’avons mentionné plus avant, un patron d’URL est une expression régulière; Partant de là, nous pouvons utiliser le patron d’expression rationnelle \d+ pour trouver un ou plusieurs chiffres :
from django.conf.urls.defaults import *
from mysite.views import current_datetime, hours_ahead
urlpatterns = patterns('',
(r'^time/$', current_datetime),
(r'^time/plus/\d+/$', hours_ahead),
)
Ce patron d’URL retrouvera n’importe quel URL du genre /time/plus/2/, /time/plus/25/, et même /time/plus/100000000000/. Tout bien réfléchi, nous allons ajouter une limite de façon à ce que le maximum autorisé soit fixé à 99 heures. Cela signifie que nous voulons autoriser les nombres à un ou deux chiffres — avec la syntaxe des expressions régulières, cela s’écrit \d{1,2} :
(r'^time/plus/\d{1,2}/$', hours_ahead),
Note
Lorsque l’on développe des applications web, il est toujours important de considérer la possibilité d’entrer des données farfelues, et de décider si l’application doit prendre en charge ou pas ces données. Nous avons réduit ici la fantaisie en limitant le modèle à 99 heures. Mis à part cela, « les Freineurs de Fantaisie » pourrait faire un fantastique nom d’orchestre.
Maintenant que nous avons désignés un joker pour l’URL, nous avons besoin d’une manière de transmettre cette donnée à la fonction view, de façon à pouvoir utiliser une unique fonction view pour un modèle d’heure arbitraire donné. Nous faisons cela en placant des paranthèses autour des données du patron d’URL que nous voulons enregistrer. Dans notre exemple, où nous voulons enregistrer le nombre saisi dans l’URL, quelqu’il soit, nous ajoutons des parenthèses autour de \d{1,2} :
(r'^time/plus/(\d{1,2})/$', hours_ahead),
Si vous êtes familier des expressions régulières, vous vous sentirez comme à la maison ici; nous utilisons des parenthèses pour capturer les données dans le texte correspondant.
L’URLconf final, incluant notre précédente vue current_datetime, ressemble à ceci :
from django.conf.urls.defaults import *
from mysite.views import current_datetime, hours_ahead
urlpatterns = patterns('',
(r'^time/$', current_datetime),
(r'^time/plus/(\d{1,2})/$', hours_ahead),
)
En prenant cela en compte, écrivons la vue hours_ahead.
Ordre de codage
Dans cet exemple, nous avons écrit le patron d’URL en premier et la vue ensuite, alors que dans l’exemple précédent, nous avions écrit la vue en premier, puis le patron d’URL. Quelle est la meilleure technique ? Et bien, chaque développeur est différent.
Si vous préférez les vues d’ensemble, il vous paraitra plus clair d’écrire en même temps tous les patrons d’URL de votre application, au début de votre projet, puis d’écrire ensuite les vues. Ceci à l’avantage de vous donner une liste claire des choses à faire, et de définir les paramètres requis pour les fonctions view que vous devrez écrire.
Si vous êtes plutôt un développeur « ascendant », vous préférerez peut-être écrire les vues en premier, pour ensuite les ancrer aux URLs. C’est bien aussi.
En fin de compte, il convient de prendre la technique qui s’adapte le mieux à votre pensée. Les deux approches sont valables.
hours_ahead est très similaire à la vue current_datetime que nous avons écrite auparavant, avec une différence cruciale; elle prend un argument suppplémentaire, le nombre d’heure à substituer. Ajoutez ceci à views.py :
def hours_ahead(request, offset):
offset = int(offset)
dt = datetime.datetime.now() +
datetime.timedelta(hours=offset)
html = "<html><body>In %s hour(s), it will be
%s.</body></html>" % (offset, dt)
return HttpResponse(html)
Explorons ce code une ligne après l’autre :
Comme nous l’avons fait pour notre vue current_datetime, nous importons la classe django.http.HttpResponse et le module datetime.
La fonction view, hours_ahead, prend deux paramètres en argument : request et offset.
request est un objet HttpRequest, tout comme dans current_datetime. Répétons le à nouveau : chaque vue prend toujours un objet HttpRequest en premier paramètre.
offset est la chaîne capturée par les parenthèses du patron d’URL. Par exemple, si l’URL demandée est /time/plus/3/, alors l’offset sera la chaîne '3'. Si l’URL est /time/plus/21/`, alors l’offset sera la chaîne '21'. Notez que les chaînes capturées seront toujours du type chaîne, et non pas des entiers, même si la chaîne est composée uniquement de chiffres, comme dans '21'.
Nous avons décidés d’appeler cette variable offset, mais vous pouvez l’appeler comme bon vous semble, dès lors qu’il s’agit d’un identifiant Python valide. Le nom de la variable importe peu; Ce qui compte, c’est qu’il s’agisse du second argument de la fonction (après request). Il est aussi possible d’utiliser un mot clef, plutôt que des arguments positionnels, dans l’URLconf. Nous abordons cela dans le chapitre 8.
La première chose que nous faisons à l’intérieure de la foncion est d’appeler int() sur l’offset. Cela converti la valeur de la chaîne en un entier.
Notez que Python levera une exception ValueError si vous appelez int() sur une valeur qui ne peut être convertie en entier, comme par exemple dans la chaîne 'foo'. Cependant, nous n’avons pas à nous soucier de traiter l’exception dans cet exemple, car nous pouvons être certain que l’offset sera une chaîne contenant uniquement des chiffres. Nous savons cela parce que l’expression régulière dans notre URLconf — (\d{1,2}) — capture uniquement les chiffres. Ceci illustre une autre facilité des URLconfs : ils fournissent un assez bon niveau de validation des entrées.
La ligne suivante de la fonction explique pourquoi nous avons appelés int() sur offset. Dans cette ligne, nous calculons l’heure courante plus un volant de offset heures, enregistrant le résultat dans dt. La fonction datetime.timedelta impose que le paramètre hours soit un entier.
Ensuite, nous construisons la sortie HTML de cette fonction view, tout comme nous l’avons fait pour current_datetime. La petite différence entre cette ligne et la précédente est qu’elle utilise les possibilités de formatage des chaîne de Python avec deux valeurs, et pas seulement une seule. Il y a donc deux symboles %s dans la chaîne et un tuple de valeur à insérer : (offset, dt).
Finalement, nous retournons une HttpResponse de cet HTML — encore une fois, exactement comme nous l’avions fait pour current_datetime.
Avec cette fonction view et l’URL complétée, démarrez le serveur de développement de Django (s’il ne tourne pas déjà), et consultez http://127.0.0.1:8000/time/plus/3/ pour vérifier que cela fonctionne. Ensuite, essayez http://127.0.0.1:8000/time/plus/5/. Puis http://127.0.0.1:8000/time/plus/24/. Enfin, visitez http://127.0.0.1:8000/time/plus/100/ pour vérifier que le patron dans votre URLconf n’accepte que les nombres à un — ou deux — chiffres; Django devrait afficher une erreur « Page not found » dans ce cas, comme nous l’avons vu auparavant dans la section « erreurs 404 ». L’URL http://127.0.0.1:8000/time/plus/ (sans précision d’heure) doit, elle aussi, lever une erreur 404.
Si vous suivez notre code, vous remarquerez que le fichier views.py contient à présent deux vues. (Nous avons omis la vue current_datetime du dernier jeux d’exemples pour des raisons de clareté). Mises ensemble, views.py doit ressembler à ceci :
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
def hours_ahead(request, offset):
offset = int(offset)
dt = datetime.datetime.now() +
datetime.timedelta(hours=offset)
html = "<html><body>In %s hour(s), it will be
%s.</body></html>" % (offset, dt)
return HttpResponse(html)
Jolies pages d’erreur sous Django
Prenez le temps d’admirer la belle application web que nous venons de faire — nous allons la casser à présent ! Introduisons délibérément une erreur Python dans notre fichier views.py en commentant la ligne offset = int(offset) de la vue hours_ahead :
def hours_ahead(request, offset):
#offset = int(offset)
dt = datetime.datetime.now() +
datetime.timedelta(hours=offset)
html = "<html><body>In %s hour(s), it will be
%s.</body></html>" % (offset, dt)
return HttpResponse(html)
Rechargez le serveur de développement pour naviguer vers /time/plus/3/. Vous devriez voir une page d’erreur avec une quantité d’informations significatives, comprenant un message TypeError affiché tout en haut : "unsupported type for timedelta hours component: str"; type non supporté pour le composant timedelta : str.
Que s’est-il passé ? Et bien, la fonction datetime.timedelta s’attend à ce que le paramètre hours soit un entier, et nous avons commentés la partie du code qui converti offset en entier. Ceci oblige datetime.timedelta à lever une TypeError. C’est le genre de petit bug typique auquel se confronte tout programmeur un jour où l’autre.
L’objectif de cet exemple était de montrer les pages d’erreurs de Django. Prenez le temps d’explorer la page d’erreurs et de faire connaissance avec les différentes informations qu’elle vous donne.
Quelques points remarquables :
En haut de la page, vous obtenez l’information principale concernant l’exception : le type d’exception, tous ses paramètres (le message « unsupported type » dans notre cas), le fichier dans lequel l’exception à été levée, et le numéro de la ligne responsable.
En dessous des informations sur l’exception principale, la page affiche l’intégralité du rapport Python sur cette exception. Ceci est similaire au rapport standard que vous obtenez dans l’interpréteur Python en ligne de commande, l’interactivité en plus. Pour chaque tableau de la pile, Django affiche le nom du fichier, le nom de la fonction/méthode, le numéro de ligne, ainsi que le code source de celle-ci.
Cliquez sur la ligne du code source (en gris léger), et vous verrez les quelques lignes précédentes et suivantes la ligne fautive, pour vous renseigner sur le contexte.
Cliquez sur le « Local vars » en dessous de n’importe quelle table de la pile pour voir un tableau de toutes les variables locales et de leurs valeurs, dans cette table, au point précis du code où l’exception à été levée. Cette information de déboguage est très précieuse.
Notez le « Switch to copy-and-paste view » juste en dessous de l’en-tête du rapport. Cliquez sur ces mots et le rapport d’erreur se transforme en une version alternative qui peut être facilement copiée et collée. Utilisez ceci lorsque vous voulez partager votre rapport d’erreur avec les autres pour avoir du support technique — par les bienveillantes personnes du salon de discussion IRC de Django ou par les utilisateurs de Django sur la liste de diffusion.
Enfin, la section « Request information » propose une foule d’informations au sujet de la requête entrante qui a engendrée l’erreur : information sur GET et POST, sur les valeurs des cookies et sur les métadonnées, tels que les en-têtes CGI. L’annexe H offre une référence complète sur toute l’information que contient un objet requête.
À la suite de la partie « Request information », la section « Settings » liste tous les paramétrages disponibles pour cette instance de Django. Tous les paramètrages possibles sont détaillés dans l’annexe E. Pour le moment, jettez un œil sur les paramètres pour avoir une idée des informations disponibles.
La page d’erreurs de Django a la possibilité d’afficher plus d’informations dans des cas précis, comme par exemple dans le cas d’une erreur de syntaxe. Nous détaillerons ceci plus tard, lorsque nous discuterons du système de gabarit propre à Django. Pour l’instant, décommentez la ligne offset = int(offset) pour rendre la fonction view à nouveau fonctionnelle.
Faites vous partie de ces programmeurs qui aiment à débuger à l’aide d’instructions print judicieusement placées ? Vous pouvez utiliser la page d’erreurs de Django pour ce faire — simplement sans l’instruction print. À n’importe quel endroit de votre vue, insérez temporairement un assert False pour déclencher la page d’erreur. Vous pourrez alors voir les variables locales et l’état de votre programme (il existe une méthode plus avancée permettant de déboguer les vues sous Django, que nous détaillerons plus tard, cette première méthode étant la plus rapide et la plus simple).
En conclusion, il est évident que la plupart de ces informations sont sensibles — elles exposent les entrailles de votre code Python et de votre configuration — et ce serait une pure folie que d’afficher cette information sur l’internet public. Une personne malveillante pourrait l’utiliser pour tenter d’étudier votre application web et faire de vilaines choses. Pour cette raison, la page d’erreur de Django est affichée uniquement lorsque votre projet est en mode débug. Nous expliquerons par la suite comment désactiver le mode débug. Pour l’instant, sachez simplement que tout projet Django est automatiquement en mode débug lorsque vous le démarrez (Cela vous semble familier ? Les erreurs « Page not found », décrites au paragraphe « erreurs 404 », fonctionnent de la même façon.
Et ensuite ?
Nous avons jusqu’à présent produit des vues en codant « en dur » le HTML dans le code Python. Malheureusement, c’est presque toujours une mauvaise idée. Heureusement, Django est livré avec un moteur de gabarit simple mais puissant qui vous permet de séparer la conception de la page du code sous-jacent. Nous plongerons dans le moteur de template propre à Django au chapitre suivant.
Dernière modification: 2008-12-17 16:19:12.200229