Repository with sources and generator of https://larlet.fr/david/ https://larlet.fr/david/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.html 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. <!doctype html>
  2. <html lang=fr>
  3. <head>
  4. <!-- Always define the charset before the title -->
  5. <meta charset=utf-8>
  6. <title>Rédaction de votre première appli Django, partie 3 : Création des vues de l&#39;interface publique — Biologeek — David Larlet</title>
  7. <!-- Define a viewport to mobile devices to use - telling the browser to assume that the page is as wide as the device (width=device-width) and setting the initial page zoom level to be 1 (initial-scale=1.0) -->
  8. <meta name="viewport" content="width=device-width, initial-scale=1"/>
  9. <!-- Fake favicon, to avoid extra request to the server -->
  10. <link rel="icon" href="data:;base64,iVBORw0KGgo=">
  11. <link type="application/atom+xml" rel="alternate" title="Feed" href="/david/log/" />
  12. <link rel="manifest" href="/manifest.json">
  13. <link rel="stylesheet" href="/static/david/css/larlet-david-_J6Rv.css" data-instant-track />
  14. <noscript>
  15. <style type="text/css">
  16. /* Otherwise fonts are loaded by JS for faster initial rendering. See scripts at the bottom. */
  17. body {
  18. font-family: 'EquityTextB', serif;
  19. }
  20. h1, h2, h3, h4, h5, h6, time, nav a, nav a:link, nav a:visited {
  21. font-family: 'EquityCapsB', sans-serif;
  22. font-variant: normal;
  23. }
  24. </style>
  25. </noscript>
  26. <!-- Canonical URL for SEO purposes -->
  27. <link rel="canonical" href="https://larlet.fr/david/biologeek/archives/20060617-redaction-de-votre-premiere-appli-django-partie-3-creation-des-vues-de-l-interface-publique">
  28. </head>
  29. <body>
  30. <div>
  31. <header>
  32. <nav>
  33. <p>
  34. <small>
  35. Je suis <a href="/david/" title="Profil public">David Larlet</a>, <a href="/david/pro/" title="Activité professionnelle">artisan</a> du web qui vous <a href="/david/pro/accompagnement/" title="Activité d’accompagnement">accompagne</a><span class="more-infos"> dans l’acquisition de savoirs pour concevoir des <a href="/david/pro/produits-essentiels/" title="Qu’est-ce qu’un produit essentiel ?">produits essentiels</a></span>. <span class="more-more-infos">Discutons ensemble d’une <a href="/david/pro/devis/" title="En savoir plus">non-demande de devis</a>.</span> Je partage ici mes <a href="/david/blog/" title="Expériences bienveillantes">réflexions</a> et <a href="/david/correspondances/2017/" title="Lettres hebdomadaires">correspondances</a>.
  36. </small>
  37. </p>
  38. </nav>
  39. </header>
  40. <section>
  41. <h1 property="schema:name">Rédaction de votre première appli Django, partie 3 : Création des vues de l&#39;interface publique</h1>
  42. <article typeof="schema:BlogPosting">
  43. <div property="schema:articleBody">
  44. <img src="/static/david/biologeek/images/logos/django.png" alt="vignette" style="float:left; margin: 0.5em 1em;" property="schema:thumbnailUrl" />
  45. <p>Ce tutoriel commence là où s'est achevé le <a class="reference" href="https://larlet.fr/david/biologeek/archives/20060617-redaction-de-votre-premiere-appli-django-partie-2-exploration-de-l-interface-d-admin-auto-generee/">Tutoriel 2</a>. Nous continuons
  46. l'application Web de sondage et allons nous focaliser sur la création
  47. d'interfaces publiques -- les « vues ».</p>
  48. <div class="section">
  49. <h1><a id="philosophie" name="philosophie">Philosophie</a></h1>
  50. <p>Une vue est un « type » de page Web dans votre application Django qui en général
  51. sert une fonctionnalité spécifique et possède un template spécifique. Par
  52. exemple, dans une application de weblog, vous pourriez avoir les vues suivantes:</p>
  53. <blockquote>
  54. <ul class="simple">
  55. <li>Page d'accueil du blog -- afficher quelques unes des dernières entrées.</li>
  56. <li>Page de « détail » d'un billet -- page au lien permanent pour un certain
  57. billet.</li>
  58. <li>Page d'archivage par années -- affiche tous les mois ayant des entrées
  59. dans une année donnée.</li>
  60. <li>Page d'archivage par mois -- affiche tous les jours ayant des entrées dans
  61. un mois donné.</li>
  62. <li>Page d'archivage par jour -- affiche toutes les entrées pour un jour
  63. donné.</li>
  64. <li>Action de commentaire -- gère l'envoi de commentaires pour un billet
  65. donné.</li>
  66. </ul>
  67. </blockquote>
  68. <p>Dans notre application de sondage, nous aurons les quatre vues suivantes:</p>
  69. <blockquote>
  70. <ul class="simple">
  71. <li>Page « d'archive » des sondages -- affiche quelques uns des derniers
  72. sondages.</li>
  73. <li>Page « de détail » de sondage -- affiche la question d'un sondage, sans
  74. les résultats mais avec un formulaire pour voter.</li>
  75. <li>Page « de résultat » de sondage -- affiche les résultats pour un sondage
  76. particulier.</li>
  77. <li>Action de vote -- gère le vote d'un choix particulier pour un sondage
  78. particulier.</li>
  79. </ul>
  80. </blockquote>
  81. <p>Dans Django, chaque vue est représentée par une simple fonction Python.</p>
  82. </div>
  83. <div class="section">
  84. <h1><a id="conception-de-vos-urls" name="conception-de-vos-urls">Conception de vos URLs</a></h1>
  85. <p>La première étape dans la rédaction de vues est de concevoir la structure de vos
  86. URLs. Vous faites cela en créant un module Python, appelé URLconf. Les URLconfs
  87. décrivent comment Django associe une URL donnée avec un code Python donné.</p>
  88. <p>Lorsqu'un utilisateur effectue une requête pour une page gérée par Django, le
  89. système examine l'option <tt class="docutils literal"><span class="pre">ROOT_URLCONF</span></tt>, qui contient une chaîne dans la
  90. syntaxe à points de Python. Django charge ce module et recherche une variable de
  91. module appelé <tt class="docutils literal"><span class="pre">urlpatterns</span></tt>, que est une séquence de tuples dans le format
  92. suivant:</p>
  93. <pre class="literal-block">
  94. (expression rationnelle, fonction Python de rappel [, dictionnaire facultatif])
  95. </pre>
  96. <p>Django démarre la première expression rationnelle et parcourt la liste, en
  97. comparant l'URL de la requête avec chaque expression rationnelle jusqu'à ce
  98. qu'il en trouve une qui corresponde.</p>
  99. <p>Quand il trouve une correspondance, Django appelle la fonction Python de rappel,
  100. avec un objet <tt class="docutils literal"><span class="pre">HTTPRequest</span></tt> en premier argument, toutes les valeurs
  101. « capturées » dans l'expression rationnelle sous forme d'arguments mot-clés, et,
  102. facultativement, un argument mot-clé arbitraire contenant un dictionnaire (le
  103. troisième élément facultatif du tuple).</p>
  104. <p>Pour plus d'infos sur les objets <tt class="docutils literal"><span class="pre">HTTPRequest</span></tt>, lisez la <a class="reference" href="http://www.djangoproject.com/documentation/request_response/">documentation sur
  105. les requêtes et réponses</a>.
  106. Pour plus de détails sur les URLconfs, lisez la <a class="reference" href="http://www.djangoproject.com/documentation/url_dispatch/">documentation URLconf</a>.</p>
  107. <p>Quand vous avez lancé <tt class="docutils literal"><span class="pre">python</span> <span class="pre">manage.py</span> <span class="pre">startproject</span> <span class="pre">monsite</span></tt> au début du
  108. Tutoriel 1, ça a créé un URLconf par défaut dans <tt class="docutils literal"><span class="pre">monsite/urls.py</span></tt>. Ça a aussi
  109. défini automatiquement votre option <tt class="docutils literal"><span class="pre">ROOT_URLCONF</span></tt> pour pointer vers ce
  110. fichier:</p>
  111. <pre class="literal-block">
  112. ROOT_URLCONF = 'monsite.urls'
  113. </pre>
  114. <p>Passons à un exemple. Éditez <tt class="docutils literal"><span class="pre">monsite/urls.py</span></tt> pour qu'il ressemble à ceci:</p>
  115. <pre class="literal-block">
  116. from django.conf.urls.defaults import *
  117. urlpatterns = patterns('',
  118. (r'^polls/$', 'monsite.polls.views.index'),
  119. (r'^polls/(?P&lt;poll_id&gt;\d+)/$', 'monsite.polls.views.detail'),
  120. (r'^polls/(?P&lt;poll_id&gt;\d+)/results/$', 'monsite.polls.views.results'),
  121. (r'^polls/(?P&lt;poll_id&gt;\d+)/vote/$', 'monsite.polls.views.vote'),
  122. )
  123. </pre>
  124. <p>Cela mérite quelques explications. Lorsque quelqu'un effectue une requête sur
  125. une page de votre site Web -- disons, « /polls/23/ », Django va charger ce
  126. module Python, parce qu'il est pointé par l'option <tt class="docutils literal"><span class="pre">ROOT_URLCONF</span></tt>. Ça trouve
  127. la variable nommée <tt class="docutils literal"><span class="pre">urlpatterns</span></tt> et parcourt les expressions rationnelles dans
  128. l'ordre. Quand il trouve une expression rationnelle qui correspond --
  129. <tt class="docutils literal"><span class="pre">r'^polls/(?P&lt;poll_id&gt;\d+)/$'</span></tt> -- il charge le module ou paquetage Python
  130. associé : <tt class="docutils literal"><span class="pre">monsite.polls.views.detail</span></tt>. Ce qui correspond à la fonction
  131. <tt class="docutils literal"><span class="pre">detail()</span></tt> dans <tt class="docutils literal"><span class="pre">mysite/polls/views.py</span></tt>.</p>
  132. <p>Enfin, il appelle cette fonction <tt class="docutils literal"><span class="pre">detail()</span></tt> comme ceci:</p>
  133. <blockquote>
  134. detail(request=&lt;HttpRequest object&gt;, poll_id='23')</blockquote>
  135. <p>La partie <tt class="docutils literal"><span class="pre">poll_id='23'</span></tt> vient de <tt class="docutils literal"><span class="pre">(?P&lt;poll_id&gt;\d+)</span></tt>. Mettre les parenthèses
  136. autour d'un masque (pattern) « capture » le texte correspondant à ce masque et
  137. l'envoie en argument à la fonction de vue. Le <tt class="docutils literal"><span class="pre">?P&lt;poll_id&gt;</span></tt> définit le nom qui
  138. sera utilisé pour identifier le masque correspondant et <tt class="docutils literal"><span class="pre">\d+</span></tt> est une
  139. expression rationnelle pour trouver une séquence de chiffres (c'est-à-dire, un
  140. nombre).</p>
  141. <p>Parce que les masques d'URL sont des expressions rationnelles, il n'y a vraiment
  142. aucune limite sur ce que vous pouvez faire avec eux. Et il n'est pas nécessaire
  143. d'ajouter de fioritures à l'URL tel que <tt class="docutils literal"><span class="pre">.php</span></tt> -- sauf si vous avez un sens de
  144. l'humour tordu, dans lequel cas vous pouvez faire quelque chose comme ça:</p>
  145. <pre class="literal-block">
  146. (r'^polls/latest\.php$', 'monsite.polls.views.index'),
  147. </pre>
  148. <p>Mais, ne faites pas ça. C'est stupide.</p>
  149. <p>Notez que ces expressions rationnelles ne recherchent pas dans les paramètres
  150. GET et POST, ni dans le nom de domaine. Par exemple, dans une requête vers
  151. <tt class="docutils literal"><span class="pre">http://www.exemple.com/monappli/</span></tt>, l'URLconf va rechercher dans
  152. <tt class="docutils literal"><span class="pre">/monappli/</span></tt>. Dans une requête vers
  153. <tt class="docutils literal"><span class="pre">http://www.exemple.com/monappli/?page=3</span></tt>, l'URLconf va rechercher dans
  154. <tt class="docutils literal"><span class="pre">/myapp/</span></tt>.</p>
  155. <p>Si vous avez besoin d'aide sur les expressions rationnelles, lisez <a class="reference" href="http://en.wikipedia.org/wiki/Regular_expression">l'article de
  156. Wikipedia</a> et la <a class="reference" href="http://www.python.org/doc/current/lib/module-re.html">documentation Python</a>. De plus, le livre O'Reilly
  157. « Mastering Regular Expressions » de Jeffrey Friedl est fantastique.</p>
  158. <p>Pour finir, une note sur la performance: ces expressions rationnelles sont
  159. compilées la première fois que le modul URLconf est chargé. Ça les rend très
  160. rapides.</p>
  161. </div>
  162. <div class="section">
  163. <h1><a id="crire-votre-premi-re-vue" name="crire-votre-premi-re-vue">Écrire votre première vue</a></h1>
  164. <p>Bien, nous n'avons toujours pas créé de vue -- nous avons juste touché à
  165. URLconf. Mais assurons-nous que Django suit l'URLconf comme il faut.</p>
  166. <p>Lancez le serveur Web de développement de Django:</p>
  167. <pre class="literal-block">
  168. python manage.py runserver
  169. </pre>
  170. <p>Maintenant, allez sur « <a class="reference" href="http://localhost:8000/polls/">http://localhost:8000/polls/</a> » avec votre navigateur
  171. Web. Vous devriez obtenir une page d'erreur agréablement colorée avec
  172. le message suivant:</p>
  173. <pre class="literal-block">
  174. ViewDoesNotExist at /polls/
  175. Tried index in module mysite.polls.views. Error was: 'module'
  176. object has no attribute 'index'
  177. </pre>
  178. <p>Cette erreur survient parce que vous n'avez pas écrit la fonction <tt class="docutils literal"><span class="pre">index()</span></tt>
  179. dans le module <tt class="docutils literal"><span class="pre">monsite/polls/views.py</span></tt>.</p>
  180. <p>Essayez « /polls/23/ », « /polls/23/results/ » et « /polls/23/vote/ ». Les
  181. messages d'erreur vous disent quelle vue Django essaie de charger (et n'arrive
  182. pas à trouver, parce que vous n'avez pas encore écrit les vues).</p>
  183. <p>Il est temps d'écrire la première vue. Ouvrez le fichier
  184. <tt class="docutils literal"><span class="pre">monsite/polls/views.py</span></tt> et mettez-y le code Python suivant:</p>
  185. <pre class="literal-block">
  186. from django.http import HttpResponse
  187. def index(request):
  188. return HttpResponse(&quot;Bonjour tout le monde. Vous est dans l'index des sondages.&quot;)
  189. </pre>
  190. <p>Voilà la vue la plus simple possible. Allez dans « /polls/ » avec votre
  191. navigateur, et vous devriez voir votre texte.</p>
  192. <p>À présent, ajoutez la vue suivante. C'est un petit peu différent, parce qu'il
  193. prend un argument (qui, rappelez-vous, est passé depuis ce qui a été capturé par
  194. l'expression rationnelle dans l'URLconf):</p>
  195. <pre class="literal-block">
  196. def detail(request, poll_id):
  197. return HttpResponse(&quot;Vous consultez le sondage %s.&quot; % poll_id)
  198. </pre>
  199. <p>Jetez à oeil à votre navigateur, à « /polls/34/ ». Ça affichera l'ID que vous
  200. avez fournie dans l'URL.</p>
  201. </div>
  202. <div class="section">
  203. <h1><a id="crire-des-vues-qui-font-quelque-chose" name="crire-des-vues-qui-font-quelque-chose">Écrire des vues qui font quelque chose</a></h1>
  204. <p>Chaque vue a la responsabilité de faire l'une des deux choses suivantes :
  205. Retourner un objet <tt class="docutils literal"><span class="pre">HttpResponse</span></tt> renfermant le contenu de la page demandée,
  206. ou lever une exception telle que <tt class="docutils literal"><span class="pre">Http404</span></tt>. Le reste est pour vous.</p>
  207. <p>Votre vue peut lire ou non les enregistrements d'une base de données. Il peut
  208. utiliser ou non un système de template tel que celui de Django -- ou un système
  209. tiers de template Python. Il peut générer un fichier PDF, une sortie XML, créer
  210. un fichier ZIP à la volée, tout ce que vous voulez, en utilisant toutes les
  211. bibliothèques Python que vous désirez.</p>
  212. <p>Tout ce que Django veut, c'est <tt class="docutils literal"><span class="pre">HttpResponse</span></tt>. Ou une exception.</p>
  213. <p>Parce que c'est plus pratique, utilisons l'API de base de données propre à
  214. Django, que nous avons abordée dans le Tutoriel 1. Voici un premier jet de la
  215. vue <tt class="docutils literal"><span class="pre">index()</span></tt>, qui affiche les 5 dernières questions de sondage du système,
  216. séparées par des virgules, triées par date de publication:</p>
  217. <pre class="literal-block">
  218. from monsite.polls.models import Poll
  219. from django.http import HttpResponse
  220. def index(request):
  221. latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
  222. output = ', '.join([p.question for p in latest_poll_list])
  223. return HttpResponse(output)
  224. </pre>
  225. <p>Nous avons ici un problème, à savoir : Le design de la page est codée en dure
  226. dans la vue. Si vous voulez changer l'apparence de la page, vous devrez éditer
  227. ce code Python. Donc utilisons plutôt le système de template de Django pour
  228. la présentation des données:</p>
  229. <pre class="literal-block">
  230. from django.template import Context, loader
  231. from monsite.polls.models import Poll
  232. from django.http import HttpResponse
  233. def index(request):
  234. latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
  235. t = loader.get_template('polls/index.html')
  236. c = Context({
  237. 'latest_poll_list': latest_poll_list,
  238. })
  239. return HttpResponse(t.render(c))
  240. </pre>
  241. <p>Ce code charge la template appelé « polls/index.html » et lui passe un contexte.
  242. Le contexte est un dictionnaire indexant les objets Python selon des noms de
  243. variable de template.</p>
  244. <p>Rechargez la page. Maintenant, vous verrez une erreur:</p>
  245. <pre class="literal-block">
  246. TemplateDoesNotExist at /polls/
  247. polls/index.html
  248. </pre>
  249. <p>Ah. Il n'y a pas encore de template. En premier lieu, créez un répertoire,
  250. quelque part dans votre système de fichers, auquel Django a accès (Django
  251. s'exécute avec les droits de l'utilisateur qui a lancé le serveur). Cependant,
  252. ne les mettez pas à la racine de vos documents. Vous ne devriez probablement pas
  253. les rendre publics, juste par mesure de sécurité.</p>
  254. <p>Puis éditez <tt class="docutils literal"><span class="pre">TEMPLATE_DIRS</span></tt> dans votre <tt class="docutils literal"><span class="pre">settings.py</span></tt> pour dire à Django où
  255. il peut trouver les templates -- tout comme vous l'avez fait dans la section
  256. « Personnaliser l'apparence de l'interface d'admin » du Tutoriel 2.</p>
  257. <p>Une fois fait, créez un répertoire <tt class="docutils literal"><span class="pre">polls</span></tt> dans votre répertoire de templates.
  258. Dedans, créez un fichier appelé <tt class="docutils literal"><span class="pre">index.html</span></tt>. Notez que notre code
  259. <tt class="docutils literal"><span class="pre">loader.get_template('polls/index.html')</span></tt> ci-dessus se réfère à
  260. &quot;[répertoire_de_templates]/polls/index.html&quot; dans le système de fichiers.</p>
  261. <p>Placez le code suivant dans ce template:</p>
  262. <pre class="literal-block">
  263. {% if latest_poll_list %}
  264. &lt;ul&gt;
  265. {% for poll in latest_poll_list %}
  266. &lt;li&gt;{{ poll.question }}&lt;/li&gt;
  267. {% endfor %}
  268. &lt;/ul&gt;
  269. {% else %}
  270. &lt;p&gt;Aucun sondage disponnible.&lt;/p&gt;
  271. {% endif %}
  272. </pre>
  273. <p>Chargez la page dans votre navigateur Web, et vous devriez voir une liste à puce
  274. contenant le sondage &quot;Quoi de neuf&quot; du Tutoriel 1.</p>
  275. <div class="section">
  276. <h2><a id="un-raccourci-render-to-response" name="un-raccourci-render-to-response">Un raccourci: render_to_response()</a></h2>
  277. <p>Il est vraiment très courant de charger un template, remplir un contexte et
  278. retourner un objet <tt class="docutils literal"><span class="pre">HttpResponse</span></tt> avec le résultat du rendu de la template.
  279. Django fournit un raccourci. Voici le vue complète de <tt class="docutils literal"><span class="pre">index()</span></tt>, réécrite:</p>
  280. <pre class="literal-block">
  281. from django.shortcuts import render_to_response
  282. from monsite.polls.models import Poll
  283. def index(request):
  284. latest_poll_list = Poll.objects.all().order_by('-pub_date')
  285. return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})
  286. </pre>
  287. <p>Notez que nous n'avons plus besoin d'importer <tt class="docutils literal"><span class="pre">loader</span></tt>, <tt class="docutils literal"><span class="pre">Context</span></tt> ni
  288. <tt class="docutils literal"><span class="pre">HttpResponse</span></tt>.</p>
  289. <p>La fonction <tt class="docutils literal"><span class="pre">render_to_response()</span></tt> prend un nom de template en premier
  290. argument et un dictionnaire comme second argument facultatif. Il retourne un
  291. objet <tt class="docutils literal"><span class="pre">HttpResponse</span></tt> du template donné rendu avec le contexte donné.</p>
  292. </div>
  293. <p></div>
  294. <div class="section">
  295. <h1><a id="lever-l-erreur-404" name="lever-l-erreur-404">Lever l'erreur 404</a></h1>
  296. <p>À présent, abordons la vue de sondage détaillé -- la page qui affiche la
  297. question d'un sondage donné. Voici la vue:</p>
  298. <pre class="literal-block">
  299. from django.http import Http404</p>
  300. <h1>...</h1>
  301. <p>def detail(request, poll_id):
  302. try:
  303. p = Poll.objects.get(pk=poll_id)
  304. except Poll.DoesNotExist:
  305. raise Http404
  306. return render_to_response('polls/detail.html', {'poll': p})
  307. </pre>
  308. <p>Le nouveau concept ici : La vue lève l'exception <tt class="docutils literal"><span class="pre">django.http.Http404</span></tt> si un
  309. sondage avec l'ID demandé n'existe pas.</p>
  310. <div class="section">
  311. <h2><a id="un-raccourci-get-object-or-404" name="un-raccourci-get-object-or-404">Un raccourci : get_object_or_404()</a></h2></p>
  312. <p>Il est vraiment très courant d'utiliser <tt class="docutils literal"><span class="pre">get_object()</span></tt> et de déclencher
  313. <tt class="docutils literal"><span class="pre">Http404</span></tt> si l'objet n'existe pas. Django fournit un raccourci. Voici la vue
  314. <tt class="docutils literal"><span class="pre">detail()</span></tt>, réécrite:</p>
  315. <pre class="literal-block">
  316. from django.shortcuts import render_to_response, get_object_or_404
  317. # ...
  318. def detail(request, poll_id):
  319. p = get_object_or_404(Poll, pk=poll_id)
  320. return render_to_response('polls/detail.html', {'poll': p})
  321. </pre>
  322. <p>La fonction <tt class="docutils literal"><span class="pre">get_object_or_404()</span></tt> prend un modèle Django en premier argument
  323. et un nombre arbitaire d'arguments mot-clés, qu'elle passe à la fonction
  324. <tt class="docutils literal"><span class="pre">get_object()</span></tt> du module. Elle déclenche <tt class="docutils literal"><span class="pre">Http404</span></tt> si l'object n'existe pas.</p>
  325. <div class="admonition-philosophie admonition">
  326. <p class="first admonition-title">Philosophie</p>
  327. <p>Pourquoi utilisons-nous une fonction utilitaire <tt class="docutils literal"><span class="pre">get_object_or_404()</span></tt> au
  328. lieu de récupérer automatiquement les exceptions <tt class="docutils literal"><span class="pre">DoesNotExist</span></tt> de haut
  329. niveau, ou avoir l'API du modèle qui déclenche <tt class="docutils literal"><span class="pre">Http404</span></tt> au lieu de
  330. <tt class="docutils literal"><span class="pre">DoesNotExist</span></tt> ?</p>
  331. <p class="last">Parce que ça serait coupler la couche du modèle avec la couche de la vue.
  332. Une des règles d'or de la conception de Django est de maintenir ce
  333. découplage entre les différentes couches.</p>
  334. </div>
  335. <p>Il y a également une fonction <tt class="docutils literal"><span class="pre">get_list_or_404()</span></tt>, qui fonctionne
  336. similairement à <tt class="docutils literal"><span class="pre">get_object_or_404()</span></tt> -- sauf qu'elle utilise <tt class="docutils literal"><span class="pre">filter()</span></tt>
  337. au lieu de <tt class="docutils literal"><span class="pre">get()</span></tt>. Elle lève <tt class="docutils literal"><span class="pre">Http404</span></tt> si la liste est vide.</p>
  338. <p></div>
  339. </div>
  340. <div class="section">
  341. <h1><a id="crire-une-vue-404-page-non-trouv-e" name="crire-une-vue-404-page-non-trouv-e">Écrire une vue 404 (page non trouvée)</a></h1>
  342. <p>Lorsque vous levez une <tt class="docutils literal"><span class="pre">Http404</span></tt> à partir d'une vue, Django va charger une vue
  343. spéciale qui s'occupe des erreurs 404. Il la trouve en cherchant la variable
  344. <tt class="docutils literal"><span class="pre">handler404</span></tt>, qui est une chaîne de caractères utilisant la syntaxe Python à
  345. points -- le même format qui est utilisé par l'URLconf. Une vue 404 en elle-même
  346. n'a rien de particulier : c'est juste une vue classique.</p>
  347. <p>Vous n'avez normalement pas à vous soucier de l'écriture des vues 404.
  348. Par défaut les URLconfs ont la ligne suivante en en-tête :</p>
  349. <blockquote>
  350. from django.conf.urls.defaults import *</blockquote></p>
  351. <p>Cela permet de prendre en compte le paramètre <tt class="docutils literal"><span class="pre">handler404</span></tt> pour le module
  352. courrant. Comme vous pouvez le constater dans <tt class="docutils literal"><span class="pre">django/conf/urls/defaults.py</span></tt>,
  353. <tt class="docutils literal"><span class="pre">handler404</span></tt> correspond par défaut à
  354. <tt class="docutils literal"><span class="pre">'django.views.defaults.page_not_found'</span></tt>.</p>
  355. <p>Encore trois remarques au sujet des vues 404 :</p>
  356. <blockquote>
  357. <ul class="simple">
  358. <li>La vue 404 est aussi appelée si aucune des expressions rationnelle de
  359. l'URLconf ne correspond à l'adresse demandée.</li>
  360. <li>Si vous ne définissez pas votre propre vue 404 -- et utilisez celle par
  361. défaut, ce qui est recommandé -- vous devez quand même créer un template
  362. <tt class="docutils literal"><span class="pre">404.html</span></tt> à la racide de votre dossier de templates.</li>
  363. <li>Si <tt class="docutils literal"><span class="pre">DEBUG</span></tt> est à <tt class="docutils literal"><span class="pre">True</span></tt> (dans votre module de paramètres), votre vue
  364. 404 utilisera ce template pour toutes les erreurs 404.</li>
  365. </ul>
  366. </blockquote>
  367. <p></div>
  368. <div class="section"></p>
  369. <h1><a id="crire-une-vue-500-erreur-serveur" name="crire-une-vue-500-erreur-serveur">Écrire une vue 500 (erreur serveur)</a></h1>
  370. <p>De la même manière, URLconfs peut définir une <tt class="docutils literal"><span class="pre">handler500</span></tt>, qui pointe sur une
  371. vue appelée en cas d'erreur du serveur. Les erreurs du serveur lorsque vous avez
  372. des erreurs d'exécution dans le code de vos vues.</p>
  373. <p></div>
  374. <div class="section">
  375. <h1><a id="utiliser-le-syst-me-de-template" name="utiliser-le-syst-me-de-template">Utiliser le système de template</a></h1>
  376. <p>Revenons à notre vue <tt class="docutils literal"><span class="pre">polls.detail</span></tt>. Considérant la variable de contexte
  377. <tt class="docutils literal"><span class="pre">poll</span></tt>, voici le template tel qu'il pourrait être:</p>
  378. <pre class="literal-block"></p>
  379. <p>&lt;h1&gt;{{ poll.question }}&lt;/h1&gt;
  380. &lt;ul&gt;
  381. {% for choice in poll.choice_set.all %}
  382. &lt;li&gt;{{ choice.choice }}&lt;/li&gt;
  383. {% endfor %}
  384. &lt;/ul&gt;
  385. </pre>
  386. <p>Le système de template utilise une syntaxe par points pour accéder aux attributs
  387. des variables. Dans l'exemple <tt class="docutils literal"><span class="pre">{{</span> <span class="pre">poll.question</span> <span class="pre">}}</span></tt>, Django commence par
  388. chercher la clé <tt class="docutils literal"><span class="pre">question</span></tt> du dictionnaire <tt class="docutils literal"><span class="pre">poll</span></tt>. N'y parvenant pas, il
  389. essaye avec les attributs de classe -- ce qui marche dans ce cas. Si ça n'avait
  390. pas été le cas, il aurait essayé d'appeler la méthode <tt class="docutils literal"><span class="pre">question()</span></tt> de l'objet
  391. poll.</p></p>
  392. <p>L'appel d'une méthode se produit dans la boucle <tt class="docutils literal"><span class="pre">{%</span> <span class="pre">for</span> <span class="pre">%}</span></tt> :
  393. <tt class="docutils literal"><span class="pre">poll.choice_set.all</span></tt> est interprété comme le code <tt class="docutils literal"><span class="pre">poll.choice_set.all()</span></tt>,
  394. ce qui renvoie un iterable d'objets « Choice » ce qui est parfait pour une
  395. utilisation dans un tag <tt class="docutils literal"><span class="pre">{%</span> <span class="pre">for</span> <span class="pre">%}</span></tt>.</p>
  396. <p>Lisez le <a class="reference" href="http://www.djangoproject.com/documentation/templates/">guide des templates</a> pour davantage de détails sur le fonctionnement
  397. des templates.</p>
  398. <p></div>
  399. <div class="section">
  400. <h1><a id="simplifier-urlconfs" name="simplifier-urlconfs">Simplifier URLconfs</a></h1>
  401. <p>Prennons le temps de jouer un peu avec les vues et le système de templates. Lors
  402. de l'édition d'URLconf, vous avez dû vous rendre compte de la redondance
  403. présente dans le fichier:</p>
  404. <pre class="literal-block">
  405. urlpatterns = patterns('',
  406. (r'^polls/$', 'mysite.polls.views.index'),
  407. (r'^polls/(?P&lt;poll_id&gt;\d+)/$', 'mysite.polls.views.detail'),
  408. (r'^polls/(?P&lt;poll_id&gt;\d+)/results/$', 'mysite.polls.views.results'),
  409. (r'^polls/(?P&lt;poll_id&gt;\d+)/vote/$', 'mysite.polls.views.vote'),
  410. )</p>
  411. <p></pre>
  412. <p>Le répertoire d'accès <tt class="docutils literal"><span class="pre">mysite.polls.views</span></tt> est dans chaque URL.</p>
  413. <p>Étant un cas courrant, le framework URLconf possède un raccourci pour les
  414. préfixes communs. Vous pouvez les factoriser et les placer en premier argument
  415. de <tt class="docutils literal"><span class="pre">patterns()</span></tt> de la façon suivante:</p>
  416. <pre class="literal-block">
  417. urlpatterns = patterns('mysite.polls.views',
  418. (r'^polls/$', 'index'),
  419. (r'^polls/(?P&lt;poll_id&gt;\d+)/$', 'detail'),
  420. (r'^polls/(?P&lt;poll_id&gt;\d+)/results/$', 'results'),
  421. (r'^polls/(?P&lt;poll_id&gt;\d+)/vote/$', 'vote'),
  422. )</p>
  423. <p></pre>
  424. <p>C'est identique au précédent formatage. C'est juste un peu plus propre.</p>
  425. </div>
  426. <div class="section">
  427. <h1><a id="d-coupler-urlconfs" name="d-coupler-urlconfs">Découpler URLconfs</a></h1>
  428. <p>En parlant de ça, prennons le temps de découpler les URLs de notre appli de
  429. sondage à partir de la configuration de notre projet Django. Les applis Django
  430. sont censées être « pluggables » -- ce qui signifie que chaque application peut
  431. être transférée sur une autre installation de Django avec le minimum de
  432. désagréments.</p>
  433. <p>Notre appli de sondage est pas mal découplée pour le moment grâce à la structure
  434. générée par <tt class="docutils literal"><span class="pre">python</span> <span class="pre">manage.py</span> <span class="pre">startapp</span></tt>. Une partie est néanmois liée aux
  435. paramètres de Django : l'URLconf.</p></p>
  436. <p>Nous avons édité les URLs dans <tt class="docutils literal"><span class="pre">mysite/urls.py</span></tt>, mais la conception des URLs
  437. d'une appli est spécifique à l'application, pas à l'installation de Django --
  438. déplaçons donc les URLs dans le dossier de notre appli.</p>
  439. <p>Copiez le fichier <tt class="docutils literal"><span class="pre">mysite/urls.py</span></tt> vers <tt class="docutils literal"><span class="pre">mysite/polls/urls.py</span></tt>. Puis,
  440. modifiez <tt class="docutils literal"><span class="pre">mysite/urls.py</span></tt> pour retirer les URLs spécifiques au sondage et
  441. insérez un <tt class="docutils literal"><span class="pre">include()</span></tt>:</p>
  442. <pre class="literal-block">
  443. (r'^polls/', include('mysite.polls.urls')),
  444. </pre>
  445. <p><tt class="docutils literal"><span class="pre">include()</span></tt>, permet simplement d'établir une référence à une autre URLconf.
  446. Notez l'absence de <tt class="docutils literal"><span class="pre">$</span></tt> (détection de fin de chaîne de caractère) dans
  447. l'expression rationnelle mais la présence d'un slash final. Lorsque Django
  448. rencontre un <tt class="docutils literal"><span class="pre">include()</span></tt>, il extrait tout ce qui suit l'URL détectée et
  449. l'envoie à l'URLconf inclus pour un traitement ultérieur.</p>
  450. <p>Voici ce qu'il se passe si un utilisateur se rend sur « /polls/34/ » avec ce
  451. système :</p>
  452. <ul class="simple">
  453. <li>Django va rencontrer <tt class="docutils literal"><span class="pre">'^polls/'</span></tt></li>
  454. <li>Il va enlever la partie reconnue (<tt class="docutils literal"><span class="pre">&quot;polls/&quot;</span></tt>) et envoyer le texte restant --
  455. <tt class="docutils literal"><span class="pre">&quot;34/&quot;</span></tt> -- à l'URLconf monsite.polls.urls' pour un traitement ultérieur.</li>
  456. </ul>
  457. <p>Maitenant que le découplage est effectué, il est nécessaire de découpler
  458. l'URLconf de 'monsite.polls.urls' en enlevant les préfixes « polls/ » à chaque
  459. ligne:</p>
  460. <pre class="literal-block">
  461. urlpatterns = patterns('monsite.polls.views',
  462. (r'^$', 'index'),
  463. (r'^(?P&lt;poll_id&gt;\d+)/$', 'detail'),
  464. (r'^(?P&lt;poll_id&gt;\d+)/results/$', 'results'),
  465. (r'^(?P&lt;poll_id&gt;\d+)/vote/$', 'vote'),
  466. )
  467. </pre>
  468. <p>L'idée derrière <tt class="docutils literal"><span class="pre">include()</span></tt> et le découplage d'URLconf est de faciliter
  469. l'aspect plug-and-play des URLs. Maintenant que les sondages ont leur propre
  470. URLconf, ils peuvent être placés à la suite de « /polls », ou de « /fun_polls »,
  471. ou de « /content/polls », ou de n'importe quelle URL, et l'appli sera toujours
  472. fonctionnelle.</p>
  473. <p>Toute l'appli de sondage se base sur des URL relatives, non sur des URLs
  474. absolues.</p>
  475. <p>Lorsque vous avez assimilé l'écriture des vues, lisez la
  476. <a class="reference" href="https://larlet.fr/david/biologeek/archives/20060617-redaction-de-votre-premiere-appli-django-partie-4-conception-d-un-formulaire-et-vues-generiques/">partie 4 de ce tutoriel</a> de façon à en apprendre plus sur la génération de
  477. formulaires et les vues génériques.</p>
  478. <p>Vous pouvez maintenant retourner à la <a class="reference" href="https://larlet.fr/david/biologeek/archives/20060617-traduction-francaise-de-la-documentation-de-django-le-framework-web-python/">page d'accueil des traductions de la
  479. documentation de Django</a>.</p>
  480. <p>Cette traduction correspond à la révision 3589 (post 0.95).</p>
  481. </div>
  482. </div>
  483. </article>
  484. <footer>
  485. <h6 property="schema:datePublished">— 17/06/2006</h6>
  486. </footer>
  487. </section>
  488. <section>
  489. <div>
  490. <h3>Articles peut-être en rapport</h3>
  491. <ul>
  492. <li><a href="/david/biologeek/archives/20060815-le-langage-de-template-django-pour-les-auteurs-de-templates/" title="Accès à Le langage de template Django : Pour les auteurs de templates">Le langage de template Django : Pour les auteurs de templates</a></li>
  493. <li><a href="/david/biologeek/archives/20060715-comparaison-de-turbogears-et-django-deux-frameworks-web-python/" title="Accès à Comparaison de TurboGears et Django, deux frameworks web Python">Comparaison de TurboGears et Django, deux frameworks web Python</a></li>
  494. <li><a href="/david/biologeek/archives/20060617-redaction-de-votre-premiere-appli-django-partie-4-conception-d-un-formulaire-et-vues-generiques/" title="Accès à Rédaction de votre première appli Django, partie 4 : Conception d&#39;un formulaire et vues génériques">Rédaction de votre première appli Django, partie 4 : Conception d&#39;un formulaire et vues génériques</a></li>
  495. </ul>
  496. </div>
  497. </section>
  498. <section>
  499. <div id="comments">
  500. <h3>Commentaires</h3>
  501. <div class="comment" typeof="schema:UserComments">
  502. <p class="comment-meta">
  503. <span class="comment-author" property="schema:creator">David</span> le <span class="comment-date" property="schema:commentTime">19/07/2006</span> :
  504. </p>
  505. <div class="comment-content" property="schema:commentText">
  506. <p>Tu as laissé un morceau d'anglais dans la partie erreur 500 :-)</p>
  507. </div>
  508. </div>
  509. <div class="comment" typeof="schema:UserComments">
  510. <p class="comment-meta">
  511. <span class="comment-author" property="schema:creator">David, biologeek</span> le <span class="comment-date" property="schema:commentTime">19/07/2006</span> :
  512. </p>
  513. <div class="comment-content" property="schema:commentText">
  514. <p>En effet, merci c'est corrigé :)</p>
  515. </div>
  516. </div>
  517. <div class="comment" typeof="schema:UserComments">
  518. <p class="comment-meta">
  519. <span class="comment-author" property="schema:creator">gigi</span> le <span class="comment-date" property="schema:commentTime">04/01/2007</span> :
  520. </p>
  521. <div class="comment-content" property="schema:commentText">
  522. <p>Copiez le fichier mysite/urls.py vers mysite/polls/urls.py. Puis, modifiez mysite/urls.py pour retirer les URLs spécifiques au sondage et insérez un include():<br />
  523. <br />
  524. (r'^polls/', include('mysite.polls.urls')),<br />
  525. <br />
  526. à remplacer par : <br />
  527. <br />
  528. Copiez le fichier mysite/urls.py vers mysite/polls/urls.py. Puis, modifiez mysite/urls.py comme indiquer ci-dessous<br />
  529. <br />
  530. from django.conf.urls.defaults import *<br />
  531. <br />
  532. urlpatterns = patterns('',<br />
  533. (r'', include('mysite.polls.urls')),<br />
  534. )</p>
  535. </div>
  536. </div>
  537. <div class="comment" typeof="schema:UserComments">
  538. <p class="comment-meta">
  539. <span class="comment-author" property="schema:creator">cgasmi</span> le <span class="comment-date" property="schema:commentTime">24/05/2007</span> :
  540. </p>
  541. <div class="comment-content" property="schema:commentText">
  542. <p>il y a une petite erreur de traduction:<br />
  543. une requête vers <a href="http://www.exemple.com/monappli/?page=3," title="http://www.exemple.com/monappli/?page=3," rel="nofollow">www.exemple.com/monappli/...</a> l'URLconf va rechercher dans /myapp/.<br />
  544. <br />
  545. /myapp/ a la place de /monappli/<br />
  546. <br />
  547. Sinon merci pour cette doc, elle m'a beaucoup aider dans mon debut avec django.</p>
  548. </div>
  549. </div>
  550. <div class="comment" typeof="schema:UserComments">
  551. <p class="comment-meta">
  552. <span class="comment-author" property="schema:creator">Raysuken</span> le <span class="comment-date" property="schema:commentTime">26/06/2007</span> :
  553. </p>
  554. <div class="comment-content" property="schema:commentText">
  555. <p>Tres bon tuto pour commercer a utiliser le framework django !!<br />
  556. <br />
  557. Merci</p>
  558. </div>
  559. </div>
  560. </div>
  561. </section>
  562. <footer>
  563. <nav>
  564. <p>
  565. <small>
  566. Je réponds quasiment toujours aux <a href="m&#x61;ilto:d&#x61;vid%40l&#x61;rlet&#46;fr" title="Envoyer un email">emails</a> (<a href="/david/signature/" title="Ma signature actuelle avec possibilité de chiffrement">signés</a>) et vous pouvez me rencontrer à Montréal. <span class="more-infos">N’hésitez pas à <a href="/david/log/" title="Être tenu informé des mises à jour">vous abonner</a> pour être tenu informé des publications récentes.</span>
  567. </small>
  568. </p>
  569. </nav>
  570. </footer>
  571. </div>
  572. <script src="/static/david/js/larlet-david-3ee43f.js" data-no-instant></script>
  573. <script data-no-instant>InstantClick.init()</script>
  574. </body>
  575. </html>