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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. <!doctype html>
  2. <html lang=fr>
  3. <head>
  4. <!-- Always define the charset before the title -->
  5. <meta charset=utf-8>
  6. <title>★ Astuces et bonnes pratiques Django — 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/20080211-astuces-et-bonnes-pratiques-django">
  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">★ Astuces et bonnes pratiques Django</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>Développant avec <a href="http://www.django-fr.org/">Django</a> depuis maintenant près de deux ans (ça rajeunit pas tout ça...), je suis encore surpris de découvrir de nouvelles possibilités de temps en temps. Dans mon combat pour les <a href="https://larlet.fr/david/biologeek/archives/20060121-bonnes-pratiques-de-la-programmation-en-python/">bonnes pratiques</a>, je pense qu'il y a quelques bases à avoir pour se lancer dans un projet d'envergure avec Django. Je vais essayer de lister les miennes, n'hésitez pas à ajouter les vôtres pour que ça devienne une ressource collaborative.</p>
  46. <h2>Une arborescence de fichiers qui tient la route</h2>
  47. <p>Je pense qu'il y a deux stratégies lorsqu'on démarre un projet&nbsp;:</p>
  48. <ul>
  49. <li>une seule énorme application (au sens Django du terme)&nbsp;;</li>
  50. <li>plusieurs applications si possibles découplées.</li>
  51. </ul>
  52. <p>Les deux approches sont intéressantes et nécessitent de toute façon d'avoir un découpage des fichiers habituels (models, views, etc) en modules python pour s'y retrouver et un dossier de bibliothèques tierces car la réutilisation a quand même du bon. Bon ça aussi c'est discutable, certains préférant les fichiers de 3000 lignes, encore une fois ce ne sont que des conseils...</p>
  53. <p>Au final, la structure que j'adopte maintenant est la suivante (je ne donne pas le détail des applications car il varie beaucoup en fonction du degré de <a href="https://larlet.fr/david/biologeek/archives/20070629-architecture-orientee-ressource-pour-faire-des-services-web-restful/">RESTification</a> de votre projet)&nbsp;:</p>
  54. <pre>monsuperprojet/
  55. projetdjango/
  56. __init__.py
  57. manage.py
  58. applidjango1/
  59. applidjango2/
  60. applidjango3/
  61. applitierce1/
  62. applitierce2/</pre>
  63. <p>De cette manière, vous n'avez qu'un seul dossier à mettre dans votre <strong>$PYTHONPATH</strong>&nbsp;: monsuperprojet et vos applications sont totalement découplées (vous n'avez jamais besoin de connaître projetdjango). Remarquez qu'il est possible de spécifier le chemin directement dans votre <strong>manage.py</strong> si vous souhaitez ne pas surcharger votre <strong>$PYTHONPATH</strong> (par exemple si vous bossez sur plusieurs projets qui ont les mêmes noms d'applis...)&nbsp;:</p>
  64. <pre>
  65. <code>import sys
  66. sys.path = ['/chemin/vers/monsuperprojet'] + sys.path</code>
  67. </pre>
  68. <p>À ajouter bien sûr avant d'importer les settings.</p>
  69. <p>Pour revenir à l'arborescence, je n'ai pas vraiment de bonnes pratiques pour la place des templates. Certains les séparent complètement, d'autres les mettent dans les applications. À vous de voir en fonction de vos besoins.</p>
  70. <h2>Des fichiers statiques séparés</h2>
  71. <p>La doc de Django recommande de gérer ses fichiers statiques à part pour gagner en performances (téléchargements en parallèle sur un autre sous-domaine, etc). Bon bien sûr c'est un peu overkill pour un blog et encore plus lorsque vous développez. Il existe une solution assez simple pour s'en sortir avec le serveur de développement&nbsp;:</p>
  72. <pre><code>if settings.DEBUG:
  73. from django.views.static import serve
  74. urlpatterns += patterns('',
  75. (r'^media/(?P&lt;path&gt;.*)$',
  76. serve,
  77. dict(
  78. document_root = os.path.join(settings.PROJECT_PATH, 'media'),
  79. show_indexes = True
  80. )
  81. ),
  82. )</code>
  83. </pre>
  84. <p>avec <em>settings.PROJECT_PATH</em> défini ainsi&nbsp;:</p>
  85. <pre><code>PROJECT_PATH = os.path.dirname(os.path.abspath(__file__))</code></pre>
  86. <p>Puisqu'on parle des settings, n'oubliez pas qu'il faut toujours les importer avec&nbsp;:</p>
  87. <pre><code>from django.conf import settings</code></pre>
  88. <p>Et <strong>jamais</strong> directement de votre projet.</p>
  89. <p>Si vous avez besoin de versionner vos fichiers (ici aussi pour les performances car il vaut mieux avoir un cache très long et un nom de fichier qui change lorsque vous mettez à jour l'application), inutile de vous casser la tête avec des <em>style.20080214.css</em> ou autres noms de fichiers barbares, le paramètre <strong>MEDIA_URL</strong> est l'unique modification à effectuer, vous pouvez par exemple le suffixer avec le numéro de révision de votre dépôt (je vous laisse jouer avec <em>django.utils.version.get_svn_revision</em>) ou la date, on obtient&nbsp;:</p>
  90. <pre>http://media.biologeek.com/20080210/css/biologeek.css
  91. &lt;------------ MEDIA_URL ----------&gt; &lt;--- static ----&gt;</pre>
  92. <p><strong>[edit du 26 mars]</strong>&nbsp;: voir aussi <a href="http://www.djangosnippets.org/snippets/666/">ce snippet</a> à ce sujet.</p>
  93. <h2>Des raccourcis biens pratiques</h2>
  94. <p>Le système d'expressions régulières des URL de Django est très puissant. Mais il faut avouer qu'il est un peu rebutant au premier abord et qu'on a vite fait d'oublier un - ou un \+. J'ai trouvé une solution assez élégante à ça en constituant un <strong>dictionnaire d'expression régulières communément utilisées</strong> réutilisable au besoin, ça donne&nbsp;:</p>
  95. <pre><code>import lasuperfonction
  96. re_urls = {
  97. 'username': '(?P&lt;username&gt;\w+)/',
  98. 'pk_value': '(?P&lt;pk_value&gt;\d+)/',
  99. 'foo_args': '(?P&lt;foo_args&gt;\d+)?/?',
  100. etc
  101. }
  102. urlpatterns = pattern('',
  103. url(r'%(username)s%(pk_value)s%(foo_args)s' % locals(), lasuperfonction)
  104. )</code>
  105. </pre>
  106. <p>ce qui permet d'avoir des URL un peu plus lisibles. Au passage, deux détails&nbsp;:</p>
  107. <ul>
  108. <li>l'utilisation de <strong>locals()</strong> n'est pas trop coûteuse ici car les fichiers d'urls ne contiennent en théorie que des urls&nbsp;;</li>
  109. <li>il est vraiment très puissant d'utiliser les fonctions et non leurs 'noms' car ça permet d'appliquer directement les décorateurs à ce niveau, par exemple <em>login_required(lasuperfonction)</em>.</li>
  110. </ul>
  111. <p>Concernant les raccourcis, il est bien pratique d'avoir ses propres fonctions de base comme <em>send_mail</em> si vous souhaitez ensuite faire de l'envoi en asynchrone ou <em>direct_to_template</em> si vous souhaitez un jour <a href="http://www.b-list.org/weblog/2007/nov/27/performance/#c36262">précompiler vos templates</a> sans avoir à modifier tous vos fichiers (<strong>[edit]</strong>&nbsp;: une <a href="http://www.djangosnippets.org/snippets/596/">solution élégante</a>) ou par exemple si vous souhaitez modifier le chemin par défaut vers lequel redirige le décorateur <em>permission_required</em>&nbsp;:</p>
  112. <pre>from django.contrib.auth.decorators import permission_required as django_permission_required</pre>
  113. <pre><code>def permission_required(perm):
  114. return django_permission_required(perm, login_url='/')</code></pre>
  115. <p>L'avantage de Django c'est d'être totalement en Python, ce qui permet de modifier facilement les différents points bloquants au besoin :-).</p>
  116. <h2>Des GenericForeignKey encore plus puissantes</h2>
  117. <p>J'ai passé pas mal de temps à essayer d'utiliser deux <a href="http://www.djangoproject.com/documentation/models/generic_relations/">GenericForeignKey</a> dans un même modèle donc on peut considérer ça comme une astuce (même si ça ne doit pas concerner grand monde). Lorsqu'on en arrive à un tel modèle c'est généralement qu'il y a une grande inconnue dans le contenu qui va être stocké. C'est typiquement le cas d'un log qui doit à la fois prendre en compte un item et une position par exemple&nbsp;:</p>
  118. <pre><code>class Log(models.Model):
  119. item = generic.GenericForeignKey(ct_field="item_content_type", fk_field="item_object_id")
  120. item_content_type = models.ForeignKey(ContentType, related_name="log_item")
  121. item_object_id = models.IntegerField()
  122. position = generic.GenericForeignKey(ct_field="position_content_type", fk_field="position_object_id")
  123. position_content_type = models.ForeignKey(ContentType, related_name="log_position")
  124. position_object_id = models.IntegerField()</code>
  125. </pre>
  126. <p>De cette manière, vous pouvez procéder aux requêtes habituelles&nbsp;:</p>
  127. <pre><code>queryset = Log.objects.filter(item_content_type = user_ct, item_object_id = user_id)</code></pre>
  128. <p>Mais aussi utiliser directement les objets pour la création&nbsp;:</p>
  129. <pre><code>log = Log(item = user, position = last_step)</code></pre>
  130. <p>Si vous utilisez l'interface d'administration de Django auto-générée, il peut être intéressant de créer les liens vers ces objets génériques liés. Malheureusement, on ne peut pas spécifier directement une GenericForeignKey dans <em>list_display</em>&nbsp;:</p>
  131. <pre><code> class Admin:
  132. list_display = ('item',)</code>
  133. </pre>
  134. <p>Il va donc falloir passer par une petite astuce, on commence par spécifier un nom de fonction&nbsp;:</p>
  135. <pre><code> class Admin:
  136. list_display = ('get_item_for_admin',)</code></pre>
  137. <p>Puis on crée le lien vers l'objet en question&nbsp;:</p>
  138. <pre><code> def get_item_for_admin(self):
  139. url = '../../%s/%s/%s/' % ( self.item.__class__._meta.app_label,
  140. self.item.__class__._meta.module_name,
  141. self.item.id)
  142. return '&lt;a href="%s" title=""&gt;%s&lt;/a&gt;' % (url, self.item)
  143. get_item_for_admin.short_description = _('Item')
  144. get_item_for_admin.allow_tags = True</code>
  145. </pre>
  146. <p>Hop, les liens vont être disponibles pour vos GenericForeignKey.</p>
  147. <h2>Le meilleur pour la fin</h2>
  148. <h3>Une application non testée est une application morte-née</h3>
  149. <p>Vraiment. <strong>La qualité d'une application devrait se mesurer au nombre de tests</strong>, sinon c'est ce que j'appelle du code poubelle. Prenez cette bonne habitude dès le début car c'est difficile d'expliquer à vos boss qu'il va falloir passer le mois prochain à ajouter des tests.</p>
  150. <p>Heureusement, Django offre un framework vraiment intéressant pour tester vos applications alors il serait dommage de s'en passer&nbsp;! Au bout d'un moment, les tests vont devenir énormes (du moins je vous le souhaite) et prendre pas mal du temps à se lancer, ce qui va avoir pour conséquence <del>de vous faire coder directement comme un Dieu</del> de transformer la séance d'écriture des tests en corvée. N'hésitez pas à ce moment là à ne lancer qu'une partie de vos tests (s'ils sont suffisamment découplés... mais c'est le cas bien évidemment), soit en spécifiant la classe du test unitaire&nbsp;:</p>
  151. <pre>python manage.py test users.TestAccounts</pre>
  152. <p>soit en spécifiant le fichier de doctests sous réserve d'<a href="http://code.djangoproject.com/ticket/6364">appliquer ce patch</a>&nbsp;:</p>
  153. <pre>python manage.py test users.emails</pre>
  154. <h3>Parlez vous unicode&nbsp;?</h3>
  155. <p>Il est très important aussi de penser à l'internationalisation de votre application dès le début car il est vraiment pénible de devoir repasser ensuite sur l'ensemble des chaînes pouvant être traduites (et n'oubliez pas de nommer vos fichiers de templates en <strong>.html</strong> sous peine de devoir toucher à <strong>make-message.py</strong>).</p>
  156. <h3>Des IntegerFields pour les choix</h3>
  157. <p>Je ne vais pas trop me fatiguer sur ce point car c'est <a href="http://www.b-list.org/weblog/2007/nov/02/handle-choices-right-way/">très bien expliqué par James Bennett</a>. C'est très important au niveau des performances et c'est très pénible à reprendre si vous n'avez pas pris la peine d'utiliser cette méthode dès le début. On ne l'oublie qu'une seule fois généralement.</p>
  158. <h3>Des managers pour les requêtes récurrentes</h3>
  159. <p>Ici aussi, c'est déjà traité par Jared Kuolt à <a href="http://superjared.com/entry/django-quick-tip-1-managers/">deux</a> <a href="http://superjared.com/entry/django-quick-tips-15-manager-methods/">reprises</a>. Vous avez même une solution pour <a href="http://www.djangosnippets.org/snippets/562/">enchaîner</a> ces nouvelles fonctions au besoin.</p>
  160. <p>Une autre dernière astuce si vous avez à utiliser vos modèles Django dans un script indépendant de Django&nbsp;:</p>
  161. <pre><code>import os
  162. os.environ['DJANGO_SETTINGS_MODULE'] = 'projetdjango.settings'
  163. from django.conf import settings</code>
  164. </pre>
  165. <p>Vous pouvez ensuite importer vos modèles sans problème normalement.</p>
  166. <p>Enfin, <strong>usez et abusez des ressources fournies par Django</strong> (<em>middlewares</em>, <em>context_processors</em>, <em>managers</em>, etc) et Python (<em>property</em>, <em>list-comprehension</em>, <em>decorator</em>, etc). Ce sont vos meilleurs amis... avec le cache :-).</p>
  167. <p><strong>[edit du 20 mars]</strong>&nbsp;: après une relecture de <a href="http://python.net/~goodger/projects/pycon/2007/idiomatic/">Code Like a Pythonista</a>, j'ai découvert qu'il existait une solution plus simple que <abbr title="Decorate Sort Undecorate">DSU</abbr> pour trier plusieurs modèles Django selon un champ, par exemple la date de billets et de brèves de blog&nbsp;:</p>
  168. <pre><code>self.items = list(Post.published.all()[:10]) + list(Thought.published.all()[:20])</code>
  169. </pre>
  170. <p>Voici la version originale compatible python2.3&nbsp;:</p>
  171. <pre><code>to_sort = [(item.publication_date, item) for item in self.items]
  172. to_sort.sort()
  173. self.items = [item[1] for item in reversed(to_sort)]</code>
  174. </pre>
  175. <p>Et voici celle qui utilise l'argument <strong>key</strong> apparu à la version 2.4&nbsp;:</p>
  176. <pre><code>self.items.sort(key=lambda item: item.publication_date, reverse=True)</code></pre>
  177. <p>Élégant non&nbsp;?</p>
  178. <p><strong>[edit du 26 mars]</strong>&nbsp;: Pour répondre au commentaire de Philippe&nbsp;:</p>
  179. <blockquote><p>Une petite note sur des choses à pas faire comme dans la doc serait peut-être bienvenu&nbsp;? Comme par exemple l'histoire du auto_now_add&nbsp;?</p></blockquote>
  180. <p>Je n'ai plus tout en tête mais effectivement rappeler que <a href="http://www.b-list.org/weblog/2006/nov/02/django-tips-auto-populated-fields/#c2637">auto_now_add est deprecated</a> ne fait pas de mal.</p>
  181. <blockquote><p>Un petit retour d'expérience sur les migrations de schema éventuellement&nbsp;?</p></blockquote>
  182. <p>Il n'y a pas vraiment de solution pour l'instant, ormis les grosses mises à jour du modèle c'est assez simple à faire à la main. Dès qu'on touche aux données c'est toujours assez critique donc ne pas oublier de tester sur une iso-prod avant :-).</p>
  183. <blockquote><p>Un petit retour d'expérience sur l'utilisation du trunk pour tous tes projets (notamment la maintenance que ça engendre)&nbsp;?</p></blockquote>
  184. <p>Le problème du trunk arrive lorsqu'on laisse un projet en sommeil quelques mois/années et qu'il faut alors tout mettre à jour d'un coup. Tant que ça reste du quotidien, les modifications sont généralement minimes donc c'est rapidement fait. Ma stratégie dans le cas d'une reprise c'est de reprendre point par point ceux listés sur la page des <a href="http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges">changements de Django</a> ce qui se fait sans douleur pour l'instant, à part pour le passage en unicode bien sûr...</p>
  185. <p>Il ne faut pas se voiler la vérité, le web évolue toujours plus rapidement et un projet s'entretient sous peine de devenir irrécupérable. C'est presque un avantage d'être sur une version qui avance au quotidien et qui permet de conserver cette agilité. Je n'ai jamais ressenti ça comme une contrainte personnellement, je suis davantage gêné par mon propre code qui reflète ma progression&nbsp;: «&nbsp;comment j'ai fait pour coder ça comme ça il y a 6 mois ?! » ;-).</p>
  186. </div>
  187. </article>
  188. <footer>
  189. <h6 property="schema:datePublished">— 11/02/2008</h6>
  190. </footer>
  191. </section>
  192. <section>
  193. <div>
  194. <h3>Articles peut-être en rapport</h3>
  195. <ul>
  196. <li><a href="/david/biologeek/archives/20090526-django-roa-pour-une-architecture-orientee-ressources/" title="Accès à ★ Django-ROA, pour une architecture orientée ressources">★ Django-ROA, pour une architecture orientée ressources</a></li>
  197. <li><a href="/david/biologeek/archives/20080902-sortie-de-django-10-une-annee-de-nouveautes/" title="Accès à Sortie de Django 1.0, une année de nouveautés">Sortie de Django 1.0, une année de nouveautés</a></li>
  198. <li><a href="/david/biologeek/archives/20080713-decouvrons-oauth-avec-mixin-et-django-oauth/" title="Accès à ★ Découvrons OAuth avec mixin (et django-oauth)">★ Découvrons OAuth avec mixin (et django-oauth)</a></li>
  199. </ul>
  200. </div>
  201. </section>
  202. <section>
  203. <div id="comments">
  204. <h3>Commentaires</h3>
  205. <div class="comment" typeof="schema:UserComments">
  206. <p class="comment-meta">
  207. <span class="comment-author" property="schema:creator">NiCoS</span> le <span class="comment-date" property="schema:commentTime">17/02/2008</span> :
  208. </p>
  209. <div class="comment-content" property="schema:commentText">
  210. <p>J'ai pas tout tout compris mais je me le garde sous le coude pour le jour où... :-)<br />
  211. <br />
  212. Merci !</p>
  213. </div>
  214. </div>
  215. <div class="comment" typeof="schema:UserComments">
  216. <p class="comment-meta">
  217. <span class="comment-author" property="schema:creator">Stan</span> le <span class="comment-date" property="schema:commentTime">27/02/2008</span> :
  218. </p>
  219. <div class="comment-content" property="schema:commentText">
  220. <p>Merci (un merci general pour ce site, d'ailleurs) !<br />
  221. <br />
  222. Malheureusement, meme avec cette astuce, je bute toujours sur le probleme elementaire de chargement des css.<br />
  223. <br />
  224. J'ai l'impression que seules les adresses commencant par le prefixe donne par settings.ADMIN_MEDIA_PREFIX seraient acceptees, or je prefererais avoir des &quot;media&quot; (e.g. css) distincts pour chaque appli Django (et non pas au niveau du projet).</p>
  225. </div>
  226. </div>
  227. <div class="comment" typeof="schema:UserComments">
  228. <p class="comment-meta">
  229. <span class="comment-author" property="schema:creator">Philippe Mironov</span> le <span class="comment-date" property="schema:commentTime">25/03/2008</span> :
  230. </p>
  231. <div class="comment-content" property="schema:commentText">
  232. <p>Quelques sujets que je serai heureux de lire sur ce blog :<br />
  233. <br />
  234. Une petite note sur des choses à pas faire comme dans la doc serait peut-être bienvenu ? Comme par exemple l'histoire du auto_now_add ?<br />
  235. <br />
  236. Un petit retour d'experience sur les migrations de schema eventuellement ?<br />
  237. <br />
  238. Un petit retour d'experience sur l'utilisation du trunk pour tous tes projets (nottement la maintenance que ça engendre) ?<br />
  239. <br />
  240. </p>
  241. </div>
  242. </div>
  243. <div class="comment" typeof="schema:UserComments">
  244. <p class="comment-meta">
  245. <span class="comment-author" property="schema:creator">David, biologeek</span> le <span class="comment-date" property="schema:commentTime">26/03/2008</span> :
  246. </p>
  247. <div class="comment-content" property="schema:commentText">
  248. <p>@Stan : avoir des media distincts pour chaque application c'est possible, il suffit de mettre APPLI_FOO_MEDIA_URL, APPLI_BAR_MEDIA_URL, etc dans tes settings et d'utiliser ensuite ces settings vu que tu dois de toute façon passer par un context_processor pour les récupérer dans les templates.<br />
  249. <br />
  250. @Philippe Mironov : répondu dans le billet.</p>
  251. </div>
  252. </div>
  253. <div class="comment" typeof="schema:UserComments">
  254. <p class="comment-meta">
  255. <span class="comment-author" property="schema:creator">Philippe Mironov</span> le <span class="comment-date" property="schema:commentTime">27/03/2008</span> :
  256. </p>
  257. <div class="comment-content" property="schema:commentText">
  258. <p>Un grand merci pour ces réponses.</p>
  259. </div>
  260. </div>
  261. <div class="comment" typeof="schema:UserComments">
  262. <p class="comment-meta">
  263. <span class="comment-author" property="schema:creator">Louevie</span> le <span class="comment-date" property="schema:commentTime">16/05/2008</span> :
  264. </p>
  265. <div class="comment-content" property="schema:commentText">
  266. <p>Bonjour,</p>
  267. <p>Je m&#39;intéresse en ce moment au framework pour développez des sites avec python et j&#39;ai vu que Zope était l&#39;un des premiers. Je voulais savoir quels sont les détails qui t&#39;ont fait choisir django plutôt qu&#39;un autre?</p>
  268. </div>
  269. </div>
  270. <div class="comment" typeof="schema:UserComments">
  271. <p class="comment-meta">
  272. <span class="comment-author" property="schema:creator">Nager</span> le <span class="comment-date" property="schema:commentTime">09/07/2008</span> :
  273. </p>
  274. <div class="comment-content" property="schema:commentText">
  275. <p>Salue, je débute sous python et m&#39;intéresse actuellement au web sous python. Mon problème est de pouvoir installer &quot;django&quot; sans avoir internet car je n&#39;ai pas internet et voudrais récupéré les binaires dans un cybercafé et le ramener chez moi pour travailler comment faire s.v.p je suis sous ubuntu 7.10 quand je veux un logiciel je me rend sur package.ubuntu et récupéré mon paquet et ses dépendance. Ses fastidieux mais quand on a choisit Linux on doit faire avec. Comment donc faire pour django svp</p>
  276. </div>
  277. </div>
  278. <div class="comment" typeof="schema:UserComments">
  279. <p class="comment-meta">
  280. <span class="comment-author" property="schema:creator">David, biologeek</span> le <span class="comment-date" property="schema:commentTime">10/07/2008</span> :
  281. </p>
  282. <div class="comment-content" property="schema:commentText">
  283. <p>@Louevie : j&#39;ai répondu dans ce billet <a href="https://larlet.fr/david/biologeek/archives/20080521-conferences-django-pour-pycon-fr/">https://larlet.fr/david/biologeek/archives/20080521-conferences-django-pour-pycon-fr/</a></p>
  284. <p>N&#39;hésite pas si tu veux des précisions.</p>
  285. <p>@Nager : il faut soit récupérer les sources avec svn ce qui te permettra de tester la dernière version à jour, soit télécharger l&#39;archive d&#39;une version officielle sur <a href="http://www.djangoproject.com/download/">http://www.djangoproject.com/download/</a></p>
  286. <p>La dernière version est la 0.96.2 au moment où j&#39;écris ce commentaire.</p>
  287. <p>Sinon pour Ubuntu, il y a le paquet python-django qui te permet de l&#39;installer comme un paquet classique, c&#39;est documenté ici : <a href="http://www.djangoproject.com/documentation/distributions/#ubuntu">http://www.djangoproject.com/documentation/distributions/#ubuntu</a></p>
  288. <p>Bon courage :)</p>
  289. </div>
  290. </div>
  291. <div class="comment" typeof="schema:UserComments">
  292. <p class="comment-meta">
  293. <span class="comment-author" property="schema:creator">étudiant marocain</span> le <span class="comment-date" property="schema:commentTime">27/11/2008</span> :
  294. </p>
  295. <div class="comment-content" property="schema:commentText">
  296. <p>Salut<br />je commence à m&#39;intéresser aux frameworks de développement Web, mais j&#39;hésite entre symphony et zend, pourriez vous m&#39;indiquer lequel choisir et pourquoi ? merci</p>
  297. </div>
  298. </div>
  299. <div class="comment" typeof="schema:UserComments">
  300. <p class="comment-meta">
  301. <span class="comment-author" property="schema:creator">David, biologeek</span> le <span class="comment-date" property="schema:commentTime">27/11/2008</span> :
  302. </p>
  303. <div class="comment-content" property="schema:commentText">
  304. <p>@étudiant marocain : on ne peut pas choisir sans avoir un besoin. Ou alors si, mais Django ;-).</p>
  305. </div>
  306. </div>
  307. <div class="comment" typeof="schema:UserComments">
  308. <p class="comment-meta">
  309. <span class="comment-author" property="schema:creator">étudiant marocain</span> le <span class="comment-date" property="schema:commentTime">02/12/2008</span> :
  310. </p>
  311. <div class="comment-content" property="schema:commentText">
  312. <p>Merci pour la réponse.<br />Je comprends pas ce que vous voulez dire par besoin ? <br />Ce qui me préoccupe c&#39;est la maintenabilité de mon code c&#39;est tout :-)</p>
  313. </div>
  314. </div>
  315. <div class="comment" typeof="schema:UserComments">
  316. <p class="comment-meta">
  317. <span class="comment-author" property="schema:creator">cambuntu</span> le <span class="comment-date" property="schema:commentTime">16/12/2008</span> :
  318. </p>
  319. <div class="comment-content" property="schema:commentText">
  320. <p>salut j&#39;ai un problème, Django ne gère pas dans la Module admin l&#39;unicité d&#39;un champ du modèle.<br />Si dans un modèle j&#39;ai marque un champ unique alors en le remplissant si on rentre la même valeur deux fois django génère des erreurs en mode debug pas de jolie erreurs comme lorsqu&#39;il valide un formulaire comment résoudre ce problème<br />Aussi j&#39;ai deux champs date je voudrais que le module admin valide le fait qu&#39;une date soit supérieur a l&#39;autre comment le faire</p>
  321. </div>
  322. </div>
  323. <div class="comment" typeof="schema:UserComments">
  324. <p class="comment-meta">
  325. <span class="comment-author" property="schema:creator">David, biologeek</span> le <span class="comment-date" property="schema:commentTime">17/12/2008</span> :
  326. </p>
  327. <div class="comment-content" property="schema:commentText">
  328. <p>Ça fait partie de la validation des modèles, qui sera normalement dans Django 1.1 (cf. <a href="http://code.djangoproject.com/wiki/Version1.1Roadmap">http://code.djangoproject.com/wiki/Version1.1Roadmap</a>)</p>
  329. </div>
  330. </div>
  331. <div class="comment" typeof="schema:UserComments">
  332. <p class="comment-meta">
  333. <span class="comment-author" property="schema:creator">Twinsview</span> le <span class="comment-date" property="schema:commentTime">06/03/2009</span> :
  334. </p>
  335. <div class="comment-content" property="schema:commentText">
  336. <p>Pour les frameworks : Django, Ruby On Rails ou rien :)</p>
  337. </div>
  338. </div>
  339. </div>
  340. </section>
  341. <footer>
  342. <nav>
  343. <p>
  344. <small>
  345. 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>
  346. </small>
  347. </p>
  348. </nav>
  349. </footer>
  350. </div>
  351. <script src="/static/david/js/larlet-david-3ee43f.js" data-no-instant></script>
  352. <script data-no-instant>InstantClick.init()</script>
  353. </body>
  354. </html>