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 36KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  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 2 : Exploration de l&#39;interface d&#39;admin auto-générée — 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-2-exploration-de-l-interface-d-admin-auto-generee">
  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 2 : Exploration de l&#39;interface d&#39;admin auto-générée</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ù le <a class="reference" href="https://larlet.fr/david/biologeek/archives/20060617-redaction-de-votre-premiere-appli-django-partie-1-initialisation-creation-des-modeles-et-api-de-la-base-de-donnees/">Tutoriel 1</a> s'achève. Nous continuons
  46. l'application de sondage Web et allons nous focaliser sur le site
  47. d'administration généré automatiquement par Django.</p>
  48. <div class="section">
  49. <h1><a id="activer-le-site-d-admin" name="activer-le-site-d-admin">Activer le site d'admin</a></h1>
  50. <p>Le site d'admin de Django n'est pas activé par défaut -- c'est une
  51. fonctionnalité optionnelle. Pour activer le site d'admin dans votre
  52. installation, suivez ces trois points:</p>
  53. <blockquote>
  54. <ul class="simple">
  55. <li>Ajoutez <tt class="docutils literal"><span class="pre">&quot;django.contrib.admin&quot;</span></tt> dans votre option <tt class="docutils literal"><span class="pre">INSTALLED_APPS</span></tt>.</li>
  56. <li>Lancez <tt class="docutils literal"><span class="pre">python</span> <span class="pre">manage.py</span> <span class="pre">syncdb</span></tt>. Puisque vous avez ajouté une nouvelle
  57. application à <tt class="docutils literal"><span class="pre">INSTALLED_APPS</span></tt>, les tables de la base de données ont
  58. besoin d'être mises à jour.</li>
  59. <li>Éditez votre fichier <tt class="docutils literal"><span class="pre">monsite/urls.py</span></tt> et décommentez la ligne en
  60. dessous de « Uncomment this for admin: ». Ce fichier est une URLconf; nous
  61. creuserons le sujet des URLconfs dans le prochain tutoriel. Pour
  62. l'instant, tout ce que vous avez besoin de savoir est qu'il définit la
  63. racine des URLs de vos applications.</li>
  64. </ul>
  65. </blockquote>
  66. </div>
  67. <div class="section">
  68. <h1><a id="d-marrer-le-serveur-de-d-veloppement" name="d-marrer-le-serveur-de-d-veloppement">Démarrer le serveur de développement</a></h1>
  69. <p>Démarrons le serveur de développement et explorons le site d'administration.</p>
  70. <p>Rappel du Tutoriel 1 : vous démarrez le serveur de développement comme ceci:</p>
  71. <pre class="literal-block">
  72. python manage.py runserver
  73. </pre>
  74. <p>À présent, ouvrez un navigateur Web et allez au « /admin/ » de votre domaine
  75. local -- par exemple, <a class="reference" href="http://127.0.0.1:8000/admin/">http://127.0.0.1:8000/admin/</a>. Vous devriez voir l'écran de
  76. connexion à l'interface d'admin:</p>
  77. <img alt="L'écran de connexion à l'interface d'admin de Django" src="/static/david/biologeek/images/django/admin01.png" />
  78. </div>
  79. <div class="section">
  80. <h1><a id="entrez-dans-le-site-d-admin" name="entrez-dans-le-site-d-admin">Entrez dans le site d'admin</a></h1>
  81. <p>Maintenant, essayez de vous identifier (vous avez créé un compte
  82. superutilisateur dans la première partie du tutoriel vous vous rappelez ?).
  83. Vous devriez voir la page d'index de l'interface d'admin de Django:</p>
  84. <a class="reference image-reference" href="/static/david/biologeek/images/django/admin02.png"><img alt="La page d'index de l'interface d'admin de Django" src="/static/david/biologeek/images/django/admin02t.png" /></a>
  85. <p>Par défaut, vous deviez voir deux types de contenu éditable : groupes et
  86. utilisateurs.
  87. Ce sont des caractéristiques du noyau que Django intègre par défaut.</p>
  88. </div>
  89. <div class="section">
  90. <h1><a id="rendre-l-appli-de-sondage-modifiable-via-l-interface-d-admin" name="rendre-l-appli-de-sondage-modifiable-via-l-interface-d-admin">Rendre l'appli de sondage modifiable via l'interface d'admin</a></h1>
  91. <p>Mais où est notre appli de sondage ? Il n'est pas affiché sur la page d'index de
  92. l'interface d'admin.</p>
  93. <p>Juste une chose à faire : Nous avons besoin de spécifier dans le modèle <tt class="docutils literal"><span class="pre">Poll</span></tt>
  94. que les objets <tt class="docutils literal"><span class="pre">Poll</span></tt>
  95. ont une interface d'admin. Éditez le fichier <tt class="docutils literal"><span class="pre">monsite/polls/models.py</span></tt> et
  96. faites les modifications suivantes pour ajouter une classe interne <tt class="docutils literal"><span class="pre">Admin</span></tt>:</p>
  97. <pre class="literal-block">
  98. class Poll(models.Model):
  99. # ...
  100. class Admin:
  101. pass
  102. </pre>
  103. <p>La <tt class="docutils literal"><span class="pre">classe</span> <span class="pre">Admin</span></tt> contiendra les paramètres qui contrôle comment ce modèle
  104. apparaît dans l'interface d'admin de Django. Tous les paramètres sont
  105. optionnels, cependant, créer ainsi une classe vide signifie « donne une
  106. interface d'administration à cet objet en utilisant les options par défaut ».</p>
  107. <p>Maintenant rechargez la page d'admin de Django pour voir les changements. Notez
  108. que vous n'avez pas à redémarrer le serveur de développement -- le serveur
  109. auto-recharge votre projet, du coup toute modification dans le code peut être
  110. visualisé immédiatement dans votre navigateur.</p>
  111. </div>
  112. <div class="section">
  113. <h1><a id="explorer-les-fonctionnalit-s-libres-de-l-interface-d-admin" name="explorer-les-fonctionnalit-s-libres-de-l-interface-d-admin">Explorer les fonctionnalités libres de l'interface d'admin</a></h1>
  114. <p>Maintenant que <tt class="docutils literal"><span class="pre">Poll</span></tt> a sa classe interne <tt class="docutils literal"><span class="pre">Admin</span></tt>, Django sait qu'il devrait
  115. être affiché sur la page d'index du site d'admin:</p>
  116. <a class="reference image-reference" href="/static/david/biologeek/images/django/admin03.png"><img alt="La page d'index du site d'admin de Django, maintenant avec les sondages d'affichés" src="/static/david/biologeek/images/django/admin03t.png" /></a>
  117. <p>Cliquez sur « Polls ». À présent, vous êtes sur la page « de listage pour
  118. modification » des sondages. Cette page affiche tous les sondages de la base de
  119. données et vous permet d'en choisir un pour l'éditer.
  120. Il y a le sondage « Quoi de neuf ? » que nous avons créé dans le premier
  121. tutoriel:</p>
  122. <a class="reference image-reference" href="/static/david/biologeek/images/django/admin04.png"><img alt="La page de listage pour modification" src="/static/david/biologeek/images/django/admin04t.png" /></a>
  123. <p>Cliquez sur le sondage « Quoi de neuf ? » pour l'éditer :</p>
  124. <a class="reference image-reference" href="/static/david/biologeek/images/django/admin05.png"><img alt="Formulaire d'édition pour un objet sondage poll" src="/static/david/biologeek/images/django/admin05t.png" /></a>
  125. <p>Choses à noter ici:</p>
  126. <ul class="simple">
  127. <li>Le formulaire est généré automatiquement depuis le modèle Poll.</li>
  128. <li>Les différents types de champs du modèle (<tt class="docutils literal"><span class="pre">models.DateTimeField</span></tt>,
  129. <tt class="docutils literal"><span class="pre">models.CharField</span></tt>) correspondent au widget d'entrée HTML approprié. Chaque
  130. type de champ sait comment s'afficher dans l'interface d'admin de Django.</li>
  131. <li>Chaque <tt class="docutils literal"><span class="pre">DateTimeField</span></tt> reçoit des raccourcis Javascript libre. Les dates
  132. obtiennent un raccourci « Aujourd'hui » et un calendrier en popup, et les
  133. heures obtiennent un raccourci « Maintenant » et une popup pratique qui liste
  134. les heures couramment saisies.</li>
  135. </ul>
  136. <p>La partie inférieur de la page vous propose une série d'opérations:</p>
  137. <ul class="simple">
  138. <li>Sauver -- Sauvegarde les modifications et retourne à la page de listage pour
  139. modification pour ce type d'objet.</li>
  140. <li>Sauver et continuer les modifications -- Sauvegarde les modifications et
  141. recharge la page d'administration de cet objet.</li>
  142. <li>Sauver et ajouter un nouveau -- Sauvegarde les modifications et charge un
  143. nouveau formulaire vierge pour ce type d'objet.</li>
  144. <li>Supprimer -- Affiche la page de confirmation de la suppression.</li>
  145. </ul>
  146. <p>Changez la « Date de publication » en cliquant sur les raccourcis
  147. « Aujourd'hui » et « Maintenant ». Puis cliquez sur « Sauver et continuer les
  148. modifications ». Ensuite, cliquez sur « Historique » en haut à droite de la
  149. page. Vous verrez une page listant toutes les modifications effectuées sur cet
  150. objet via l'interface d'administration de Django, accompagnées des date et
  151. heure, ainsi que du nom de l'utilisateur qui a fait ce changement:</p>
  152. <a class="reference image-reference" href="/static/david/biologeek/images/django/admin06.png"><img alt="La page d'historique pour l'objet de sondage poll" src="/static/david/biologeek/images/django/admin06t.png" /></a>
  153. </div>
  154. <div class="section">
  155. <h1><a id="personnaliser-le-formulaire-d-administration" name="personnaliser-le-formulaire-d-administration">Personnaliser le formulaire d'administration</a></h1>
  156. <p>Prenez quelques minutes pour vous émerveiller devant le code que vous n'avez
  157. pas dû écrire.</p>
  158. <p>Personnalisons un peu tout ça. Nous pouvons réordonner les champs en ajoutant
  159. explicitement un paramètre <tt class="docutils literal"><span class="pre">fields</span></tt> à <tt class="docutils literal"><span class="pre">Admin</span></tt>:</p>
  160. <pre class="literal-block">
  161. class Admin:
  162. fields = (
  163. (None, {'fields': ('pub_date', 'question')}),
  164. )
  165. </pre>
  166. <p>Cela fait que la « Date de publication » apparaît en premier au lieu d'être en
  167. second:</p>
  168. <img alt="Les champs ont été réordonnés" src="/static/david/biologeek/images/django/admin07.png" />
  169. <p>Ce n'est pas spécialement impressionnant avec seulement deux champs, mais pour
  170. un formulaire d'administration avec des douzaines de champs, choisir un ordre
  171. intuitif est un détail d'utilisation important.</p>
  172. <p>Et en parlant de formulaires avec des douzaines de champs, vous voudriez
  173. sûrement séparer le formulaire en plusieurs sous-ensembles:</p>
  174. <pre class="literal-block">
  175. class Admin:
  176. fields = (
  177. (None, {'fields': ('question',)}),
  178. ('Informations calendaires', {'fields': ('pub_date',)}),
  179. )
  180. </pre>
  181. <p>Le premier élément de chaque tuple dans <tt class="docutils literal"><span class="pre">fields</span></tt> est le titre de la
  182. sous-partie. Voici ce à quoi notre formulaire ressemble à présent:</p>
  183. <a class="reference image-reference" href="/static/david/biologeek/images/django/admin08.png"><img alt="Le formulaire a des sous-sensembles de champs à présent" src="/static/david/biologeek/images/django/admin08t.png" /></a>
  184. <p>Vous pouvez assigner des classes HTML arbitraires à chaque sous-ensemble. Django
  185. fournit une classe <tt class="docutils literal"><span class="pre">&quot;collapse&quot;</span></tt> qui affiche un sous-ensemble particulier,
  186. initialement replié. C'est une fonctionnalité utile lorsque vous avez un long
  187. formulaire qui contient un certain nombre de champs qui ne sont pas couramment
  188. utilisés:</p>
  189. <pre class="literal-block">
  190. class Admin:
  191. fields = (
  192. (None, {'fields': ('question',)}),
  193. ('Informations calendaires', {'fields': ('pub_date',), 'classes': 'collapse'}),
  194. )
  195. </pre>
  196. <img alt="Le sous-ensemble est initialement replié" src="/static/david/biologeek/images/django/admin09.png" />
  197. </div>
  198. <div class="section">
  199. <h1><a id="ajout-d-objets-li-s" name="ajout-d-objets-li-s">Ajout d'objets liés</a></h1>
  200. <p>OK, nous avons notre page d'administration de sondages Poll. Mais un sondage
  201. <tt class="docutils literal"><span class="pre">Poll</span></tt> possède plusieurs choix <tt class="docutils literal"><span class="pre">Choices</span></tt>, et la page d'admin n'affiche aucun
  202. choix.</p>
  203. <p>Pour le moment.</p>
  204. <p>Il y a deux façon de résoudre ce problème. Le premier et de donner au modèle
  205. <tt class="docutils literal"><span class="pre">Choice</span></tt> sa propre classe interne <tt class="docutils literal"><span class="pre">Admin</span></tt>, tout comme nous l'avons fait pour
  206. <tt class="docutils literal"><span class="pre">Poll</span></tt>. Voici ce que ça donnerait:</p>
  207. <pre class="literal-block">
  208. class Choice(models.Model):
  209. # ...
  210. class Admin:
  211. pass
  212. </pre>
  213. <p>Maintenant les choix sont une option disponible dans l'interface d'admin de
  214. Django. Le formulaire « Add choice » ressemble à ceci:</p>
  215. <img alt="Page d'administration de Choice" src="/static/david/biologeek/images/django/admin10.png" />
  216. <p>Dans ce formulaire, le champ « Poll » est une boîte de sélection contenant tous
  217. les sondages de la base de données. Django sait qu'une instance de
  218. <tt class="docutils literal"><span class="pre">ForeignKey</span></tt> devrait être représentée dans l'interface d'admin par une boîte
  219. <tt class="docutils literal"><span class="pre">&lt;select&gt;</span></tt>. Dans notre cas, seul un sondage existe à ce point.</p>
  220. <p>Notez également le lien « Add another » à côté de « Poll ». Chaque objet avec
  221. une relation ForeignKey vers un autre reçoit ce lien gratuitement. Quand vous
  222. cliquez sur « Add another », vous obtiendrez une fenêtre en popup quand le
  223. formulaire « Add poll ». Si vous ajoutez un sondage dans cette fenêtre et que
  224. vous cliquez sur « Sauver », Django sauvegardera le sondage dans la base de
  225. données et l'ajoutera dynamiquement comme choix sélectionné dans le formulaire
  226. « Add choice » que vous étiez en train de remplir.</p>
  227. <p>Mais, franchement, c'est une manière inefficace d'ajouter des objets « Choice »
  228. dans le système. Ça serait mieux si vous pouviez ajouter un groupe de choix
  229. « Choices » directement lorsque vous créez l'objet « Poll ». Faisons de cette
  230. façon.</p>
  231. <p>Retirez la classe <tt class="docutils literal"><span class="pre">Admin</span></tt> du modèle Choice. Puis, éditez le champ
  232. <tt class="docutils literal"><span class="pre">ForeignKey(Poll)</span></tt> comme ceci:</p>
  233. <pre class="literal-block">
  234. poll = models.ForeignKey(Poll, edit_inline=models.STACKED, num_in_admin=3)
  235. </pre>
  236. <p>Ça dit à Django: « Les objets Choice sont édités dans la page d'administration
  237. de Poll. Par défaut, fournir assez de champs pour 3 choix ».</p>
  238. <p>Ensuite, modifiez les autres champs dans <tt class="docutils literal"><span class="pre">Choice</span></tt> pour les mettre à
  239. <tt class="docutils literal"><span class="pre">core=True</span></tt>:</p>
  240. <pre class="literal-block">
  241. choice = models.CharField(maxlength=200, core=True)
  242. votes = models.IntegerField(core=True)
  243. </pre>
  244. <p>Ça dit à Django: « Quand tu édites un Choice dans la page d'admin de Poll, les
  245. champs 'choice' et 'votes' sont requis. La présence d'au moins un d'eux signifie
  246. que l'ajout d'un nouvel objet Choice, et que la mise à blanc de tous ces champs
  247. signifie la suppression de cet objet Choice existant ».</p>
  248. <p>Charger la page « Add poll » pour voir à quoi ça ressemble:</p>
  249. <a class="reference image-reference" href="/static/david/biologeek/images/django/admin11.png"><img alt="La page d'ajout de sondage contient maintenant des choix" src="/static/david/biologeek/images/django/admin11t.png" /></a>
  250. <p>Ça marche comme ceci : Il y a trois compartiments pour les choix « Choices »
  251. liés -- comme spécifié par <tt class="docutils literal"><span class="pre">num_in_admin</span></tt> -- mais chaque fois que vous revenez
  252. sur la page « Changement » d'un objet déjà créé, vous obtenez un compartiment
  253. supplémentaire (cela signifie que vous n'avez pas mis en dur de limite sur le
  254. nomble d'objets liés qui peuvent être ajoutés). Si vous aviez voulu de la place
  255. pour trois choix « Choices » supplémentaire à chaque fois que vous modifiez le
  256. sondage, vous auriez utilisé <tt class="docutils literal"><span class="pre">num_extra_on_change=3</span></tt>.</p>
  257. <p>Un petit problème cependant. Ça prend beaucoup de place d'afficher tous les
  258. champs pour saisir les objets « Choice » liés. C'est pour cette raison que
  259. Django offre une alternative d'affichage en ligne des objets liés:</p>
  260. <pre class="literal-block">
  261. poll = models.ForeignKey(Poll, edit_inline=models.TABULAR, num_in_admin=3)
  262. </pre>
  263. <p>Avec ce <tt class="docutils literal"><span class="pre">edit_inline=models.TABULAR</span></tt> (au lieu de <tt class="docutils literal"><span class="pre">models.STACKED</span></tt>), les
  264. objets liés sont affichés dans un format plus compact, comme un tableau:</p>
  265. <img alt="La page d'ajout de sondage a maintenant des choix plus compacts" src="/static/david/biologeek/images/django/admin12.png" />
  266. </div>
  267. <div class="section">
  268. <h1><a id="personnaliser-la-liste-pour-modification-de-l-interface-d-admin" name="personnaliser-la-liste-pour-modification-de-l-interface-d-admin">Personnaliser la liste pour modification de l'interface d'admin</a></h1>
  269. <p>Maintenant que la page d'admin des sondage « Poll » a un bon look, arrangeons un
  270. peu la page de « listage pour modification » -- celle qui affiche tous les
  271. sondages du système.</p>
  272. <p>Voici à quoi ça ressemble à ce point:</p>
  273. <a class="reference image-reference" href="/static/david/biologeek/images/django/admin04.png"><img alt="La page de listage pour modification des sondages Poll" src="/static/david/biologeek/images/django/admin04t.png" /></a>
  274. <p>Par défaut, Django affiche le <tt class="docutils literal"><span class="pre">str()</span></tt> de chaque objet. Mais parfois, ça serait
  275. plus utile si nous pouvions afficher des champs individuels. Dans ce but,
  276. utilisez l'option <tt class="docutils literal"><span class="pre">list_display</span></tt>, qui est un tuple de nom de champs à
  277. afficher, en colonnes, sur la page de listage pour modification de l'objet:</p>
  278. <pre class="literal-block">
  279. class Poll(models.Model):
  280. # ...
  281. class Admin:
  282. # ...
  283. list_display = ('question', 'pub_date')
  284. </pre>
  285. <p>Juste pour la démonstration, incluons également la méthode perso
  286. <tt class="docutils literal"><span class="pre">was_published_today</span></tt> du Tutoriel 1:</p>
  287. <pre class="literal-block">
  288. list_display = ('question', 'pub_date', 'was_published_today')
  289. </pre>
  290. <p>À présent la page de listage pour modification des sondage ressemble à ceci:</p>
  291. <a class="reference image-reference" href="/static/david/biologeek/images/django/admin13.png"><img alt="La page de listage pour modification, mise à jour" src="/static/david/biologeek/images/django/admin13t.png" /></a>
  292. <p>Vous pouvez cliquer sur les en-têtes de colonne pour trier selon ces valeurs --
  293. sauf dans le cas de l'en-tête <tt class="docutils literal"><span class="pre">was_published_today</span></tt>, parce que le tri selon le
  294. résultat d'une méthode arbitraire n'est pas supporté. Notez aussi que l'en-tête
  295. de la colonne pour <tt class="docutils literal"><span class="pre">was_published_today</span></tt> est, par défaut, le nom de la méthode
  296. (avec les underscores remplacés par des espaces. Mais vous pouvez changer cela
  297. en donnant à cette méthode un attribut <tt class="docutils literal"><span class="pre">short_description</span></tt>:</p>
  298. <pre class="literal-block">
  299. def was_published_today(self):
  300. return self.pub_date.date() == datetime.date.today()
  301. was_published_today.short_description = u'Publié aujourd\'hui ?'
  302. </pre>
  303. <p>Ajoutons une nouvelle amélioration à la page de listage pour modification de
  304. sondages : des filtres. Ajoutez la ligne suivante à <tt class="docutils literal"><span class="pre">Poll.Admin</span></tt>:</p>
  305. <pre class="literal-block">
  306. list_filter = ['pub_date']
  307. </pre>
  308. <p>Cela ajoute une sidebar « Filter » qui permet aux gens de filtrer la liste pour
  309. modification selon le champ <tt class="docutils literal"><span class="pre">pub_date</span></tt>:</p>
  310. <a class="reference image-reference" href="/static/david/biologeek/images/django/admin14.png"><img alt="La page de listage pour modification de sondages, mise à jour" src="/static/david/biologeek/images/django/admin14t.png" /></a>
  311. <p>Le type de filtre affiché dépend du type de champs que vous êtes en train de
  312. filtrer. Parce que <tt class="docutils literal"><span class="pre">pub_date</span></tt> est un DateTimeField, Django sait donner les
  313. options de filtrage par défaut pour les DateTimeFields: « Toutes les dates »,
  314. « Aujourd'hui », « Les 7 derniers jours », « Ce mois-ci », « Cette année ».</p>
  315. <p>Ça a meilleure forme. Ajoutons une fonctionnalité de recherche:</p>
  316. <pre class="literal-block">
  317. search_fields = ['question']
  318. </pre>
  319. <p>Cela ajoute une boîte de recherche en haut de la liste pour modification. Quand
  320. quelqu'un saisit des termes de recherche, Django va rechercher dans le champ
  321. <tt class="docutils literal"><span class="pre">question</span></tt>. Vous pouvez indiquer autant de champs que vous le désirez -- bien
  322. qu'il utilise un requête <tt class="docutils literal"><span class="pre">LIKE</span></tt> derrière, restez raisonnable pour garder votre
  323. base de données performante.</p>
  324. <p>Enfin, parce que les objets « Poll » ont des dates, il serait pratique
  325. d'effectuer un classement par date. Ajoutez cette ligne:</p>
  326. <pre class="literal-block">
  327. date_hierarchy = 'pub_date'
  328. </pre>
  329. <p>Cela ajoute une navigation hiérarchique, par date, en haut de la page de listage
  330. pour modification. Au premier niveau, il affiche toutes les années disponibles.
  331. Puis il affine le classement en mois et, finalement, en jours.</p>
  332. <p>C'est maintenant le bon moment de notez que les listes pour modification vous
  333. laissent une grande liberté de pagination. Par défaut, 50 items sont affichés
  334. par page. La pagination de listes pour modification, les boîtes de recherche,
  335. les filtres, les hiérarchies calendaires et le tri selon l'en-tête de colonne,
  336. tout fonctionne ensemble comme vous pensez qu'ils le devraient.</p>
  337. </div>
  338. <div class="section">
  339. <h1><a id="personnaliser-l-apparence-de-l-interface-d-administration" name="personnaliser-l-apparence-de-l-interface-d-administration">Personnaliser l'apparence de l'interface d'administration</a></h1>
  340. <p>C'est clair, avoir « Django administration » et « example.com » en haut de
  341. chaque page d'administration est ridicule. C'est juste du texte de substitution.</p>
  342. <p>C'est facile à modifier en utilisant le système de template de Django. Le site
  343. d'administration de Django est fait en Django lui-même, et ses interfaces
  344. utilisent le système de template propre à Django. (Ça devient métaphysique !)</p>
  345. <p>Ouvrez votre fichier de configuration (<tt class="docutils literal"><span class="pre">monsite/settings.py</span></tt>, souvenez-vous)
  346. et examinez l'option <tt class="docutils literal"><span class="pre">TEMPLATE_DIRS</span></tt>. <tt class="docutils literal"><span class="pre">TEMPLATE_DIRS</span></tt> est un tuple de
  347. répertoires du système de fichiers pour vérifier d'où les templates Django sont
  348. chargées. C'est un chemin de recherche.</p>
  349. <p>Par défaut, <tt class="docutils literal"><span class="pre">TEMPLATE_DIRS</span></tt> est vide. Donc, ajoutons-lui une ligne pour dire à
  350. Django où nos templates sont situées:</p>
  351. <pre class="literal-block">
  352. TEMPLATE_DIRS = (
  353. &quot;/home/mestemplates&quot;, # Remplacez par votre propre répertoire.
  354. )
  355. </pre>
  356. <p>À présent, copiez la template <tt class="docutils literal"><span class="pre">admin/base_site.html</span></tt> depuis le répertoire par
  357. défaut des templates de l'interface d'admin de Django
  358. (<tt class="docutils literal"><span class="pre">django/contrib/admin/templates</span></tt>) vers un sous-répertoire <tt class="docutils literal"><span class="pre">admin</span></tt> se
  359. trouvant dans le répertoire que vous avez défini dans <tt class="docutils literal"><span class="pre">TEMPLATE_DIRS</span></tt>. Par
  360. exemple, si votre <tt class="docutils literal"><span class="pre">TEMPLATE_DIRS</span></tt> contient <tt class="docutils literal"><span class="pre">&quot;/home/mestemplates&quot;</span></tt>, comme
  361. ci-dessus, copiez <tt class="docutils literal"><span class="pre">django/contrib/admin/templates/admin/base_site.html</span></tt> vers
  362. <tt class="docutils literal"><span class="pre">/home/mytemplates/admin/base_site.html</span></tt>. N'oubliez pas de créer au préalable
  363. ce sous-répertoire <tt class="docutils literal"><span class="pre">admin</span></tt>.</p>
  364. <p>Ensuite, éditez simplement le fichier et remplacez le texte générique de Django
  365. par le nom et l'URL de votre propre site.</p>
  366. <p>Notez que tous les templates de l'interface d'admin par défaut de Django
  367. peuvent être remplacés. Pour remplacer un template, faites simplement la même
  368. chose qu'avec <tt class="docutils literal"><span class="pre">base_site.html</span></tt> -- copiez le depuis le répertoire par défaut
  369. dans votre répertoire personnel, et faites les modifications.</p>
  370. <p>Les lecteurs avisés pourront demander : Mais si <tt class="docutils literal"><span class="pre">TEMPLATE_DIRS</span></tt> était vide par
  371. défaut, comment Django trouvait-il les templates par défaut de l'interface
  372. d'admin ? La réponse est que, par défaut, Django regarde automatiquement dans un
  373. éventuel sous-répertoire <tt class="docutils literal"><span class="pre">templates/</span></tt> à l'intérieur de chaque paquetage
  374. d'appli, pour l'utiliser en dernier recours. Lisez la <a class="reference" href="http://www.djangoproject.com/documentation/templates_python/#loader-types">documentation sur les
  375. types de chargeur</a> pour des informations complètes.</p>
  376. </div>
  377. <div class="section">
  378. <h1><a id="personnaliser-la-page-d-index-de-l-interface-d-admin" name="personnaliser-la-page-d-index-de-l-interface-d-admin">Personnaliser la page d'index de l'interface d'admin</a></h1>
  379. <p>De la même manière, vous voudriez sûrement personnaliser l'apparence de la page
  380. d'index de l'interface d'admin de Django.</p>
  381. <p>Par défaut, il affiche toutes les applis disponibles, selon la configuration de
  382. votre <tt class="docutils literal"><span class="pre">INSTALLED_APPS</span></tt>. Mais l'ordre dans lequel il affiche les éléments est
  383. aléatoire, et vous voudriez peut-être faire des modifications significatives sur
  384. la mise en page. Après tout, la page d'index est probablement la page la plus
  385. importante du site d'administration, donc autant qu'elle soit facile à utiliser.</p>
  386. <p>Le template à personnaliser est <tt class="docutils literal"><span class="pre">admin/index.html</span></tt>. (Faites la même chose
  387. qu'avec <tt class="docutils literal"><span class="pre">admin/base_site.html</span></tt> dans la précédente section -- copiez le depuis
  388. le répertoire par défaut vers votre répertoire de templates personnels.)
  389. Éditez le fichier, et vous verrez qu'il est utilisé une balise de template
  390. appelé <tt class="docutils literal"><span class="pre">{%</span> <span class="pre">get_admin_app_list</span> <span class="pre">as</span> <span class="pre">app_list</span> <span class="pre">%}</span></tt>. C'est l'instruction magique qui
  391. retrouve chaque appli Django installée. Au lieu d'utiliser ça, vous pouvez
  392. écrire en dur les liens vers les pages d'administration spécifiques aux objets
  393. de la meilleure manière que vous pensez.</p>
  394. <p>Django offre un autre raccourci dans cette procédure. Lancez la commande
  395. <tt class="docutils literal"><span class="pre">python</span> <span class="pre">manage.py</span> <span class="pre">adminindex</span> <span class="pre">polls</span></tt> pour obtenir un extrait de code de
  396. template à inclure dans la template de la page d'index de l'interface d'admin.
  397. C'est un point de départ plutôt utile.</p>
  398. <p>Pour des détails complets au sujet de la personnalisation de l'apparence du site
  399. d'administration de Django de manière générale, lisez le <a class="reference" href="http://www.djangoproject.com/documentation/admin_css/">guide CSS de
  400. l'interface d'admin de Django</a>.</p>
  401. <p>Lorsque vous vous serez familiarisé avec le site d'administration, lisez la
  402. <a class="reference" href="https://larlet.fr/david/biologeek/archives/20060617-redaction-de-votre-premiere-appli-django-partie-3-creation-des-vues-de-l-interface-publique/">partie 3 de ce tutoriel</a> pour commencer à travailler avec les vues publiques
  403. du sondage.</p>
  404. <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
  405. documentation de Django</a>.</p>
  406. <p>Cette traduction correspond à la révision 3589 (post 0.95).</p>
  407. </div>
  408. </div>
  409. </article>
  410. <footer>
  411. <h6 property="schema:datePublished">— 17/06/2006</h6>
  412. </footer>
  413. </section>
  414. <section>
  415. <div>
  416. <h3>Articles peut-être en rapport</h3>
  417. <ul>
  418. <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>
  419. <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>
  420. <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>
  421. </ul>
  422. </div>
  423. </section>
  424. <section>
  425. <div id="comments">
  426. <h3>Commentaires</h3>
  427. <div class="comment" typeof="schema:UserComments">
  428. <p class="comment-meta">
  429. <span class="comment-author" property="schema:creator">Flipper</span> le <span class="comment-date" property="schema:commentTime">05/08/2006</span> :
  430. </p>
  431. <div class="comment-content" property="schema:commentText">
  432. <p>Très bonne traduction, merci!<br />
  433. Il y a un &quot;u&quot; qui traine dans l'attribut short_description...</p>
  434. </div>
  435. </div>
  436. <div class="comment" typeof="schema:UserComments">
  437. <p class="comment-meta">
  438. <span class="comment-author" property="schema:creator">David, biologeek</span> le <span class="comment-date" property="schema:commentTime">05/08/2006</span> :
  439. </p>
  440. <div class="comment-content" property="schema:commentText">
  441. <p>Le « u » est pour le passage en unicode histoire de gérer les accents français.</p>
  442. </div>
  443. </div>
  444. <div class="comment" typeof="schema:UserComments">
  445. <p class="comment-meta">
  446. <span class="comment-author" property="schema:creator">Eric</span> le <span class="comment-date" property="schema:commentTime">11/09/2006</span> :
  447. </p>
  448. <div class="comment-content" property="schema:commentText">
  449. <p>Bonjour,<br />
  450. <br />
  451. was_published_today.short_description = u'Publié aujourd\'hui ?'<br />
  452. <br />
  453. Créé une erreur d'Unicode :<br />
  454. UnicodeEncodeError at /admin/polls/poll/<br />
  455. 'ascii' codec can't encode character u'\xe9' in position 5: ordinal not in range(128)<br />
  456. <br />
  457. novice en Python, le seul moyen que j'ai trouvé pour contourner ce probléme c'est d'utiliser la synthaxe suivante :<br />
  458. <br />
  459. was_published_today.short_description = 'Publi&amp;eacute; aujourd\'hui ?'<br />
  460. <br />
  461. Ca fonctionne ... mais il y a certainement mieux à faire ... :) <br />
  462. Eric</p>
  463. </div>
  464. </div>
  465. <div class="comment" typeof="schema:UserComments">
  466. <p class="comment-meta">
  467. <span class="comment-author" property="schema:creator">David, biologeek</span> le <span class="comment-date" property="schema:commentTime">11/09/2006</span> :
  468. </p>
  469. <div class="comment-content" property="schema:commentText">
  470. <p>Vérifie l'encodage de caractère (ou charset) de ton fichier source. S'il est en utf-8 ça devrait passer (auquel cas, il faudra déclarer la page de template en utf-8 aussi).</p>
  471. </div>
  472. </div>
  473. <div class="comment" typeof="schema:UserComments">
  474. <p class="comment-meta">
  475. <span class="comment-author" property="schema:creator">Eric</span> le <span class="comment-date" property="schema:commentTime">12/09/2006</span> :
  476. </p>
  477. <div class="comment-content" property="schema:commentText">
  478. <p>Entre temps je me suis documenté, j'utilise donc Django avec Ptyhon 2.4, sur windows Xp et ...<br />
  479. A partir de la version 2.3 de Python il est fortement conseillé aux francophones d'inclure un pseudo-commentaire au début de tous leurs scripts Python (obligatoirement à la 1ere ou à la 2ème ligne)<br />
  480. <br />
  481. # -*- coding : Latin-1 -*-<br />
  482. <br />
  483. Ou bien<br />
  484. <br />
  485. # -*- coding : Utf-8 -*-<br />
  486. <br />
  487. ce qui est tout de même une meilleure solution que la précédente ;)<br />
  488. Eric</p>
  489. </div>
  490. </div>
  491. <div class="comment" typeof="schema:UserComments">
  492. <p class="comment-meta">
  493. <span class="comment-author" property="schema:creator">Hafid</span> le <span class="comment-date" property="schema:commentTime">24/08/2010</span> :
  494. </p>
  495. <div class="comment-content" property="schema:commentText">
  496. <p>Bonjour,</p>
  497. <p>Tout d&#39;abord je vous remercie pour ce site et pour tout ce travail fourni</p>
  498. <p>Je suis en train d&#39;apprendre à travailler avec django en suivant ce tutorial Mais je rencontre un bug au niveau:<br />&quot;Rendre l&#39;appli de sondage modifiable via l&#39;interface d&#39;admin&quot;. J&#39;ajoute la classe interne Admin dans le fichiers polls/models.py mais rien ne change dans mon admin. Pensez-vous que j&#39;ai oublié un détail de configuration?<br />NB: je suis sous windows, j&#39;utilise la dernière version de django et python 2.6</p>
  499. <p>Merci d&#39;avance pour votre réponse</p>
  500. </div>
  501. </div>
  502. </div>
  503. </section>
  504. <footer>
  505. <nav>
  506. <p>
  507. <small>
  508. 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>
  509. </small>
  510. </p>
  511. </nav>
  512. </footer>
  513. </div>
  514. <script src="/static/david/js/larlet-david-3ee43f.js" data-no-instant></script>
  515. <script data-no-instant>InstantClick.init()</script>
  516. </body>
  517. </html>