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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. <!doctype html>
  2. <html lang=fr>
  3. <head>
  4. <!-- Always define the charset before the title -->
  5. <meta charset=utf-8>
  6. <title>Initialisation et modèles de données de la refonte — 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/20070412-initialisation-et-modeles-de-donnees-de-la-refonte">
  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">Initialisation et modèles de données de la refonte</h1>
  42. <article typeof="schema:BlogPosting">
  43. <div property="schema:articleBody">
  44. <img src="/static/david/biologeek/images/logos/biologeek.png" alt="vignette" style="float:left; margin: 0.5em 1em;" property="schema:thumbnailUrl" />
  45. <p>Suite de la <a href="https://larlet.fr/david/biologeek/archives/20070224-objectifs-et-motivations-de-la-refonte-de-ce-blog/">refonte de ce blog</a>, qui a pris un peu de retard. Aujourd'hui on va de l'initialisation de l'environnement de développement au modèle de données retenu. Je pars vraiment de la base pour que vous puissiez réellement suivre la refonte si vous êtes intéressé(e). C'est d'ailleurs la raison pour laquelle j'ai mis le code sur un dépôt public (cf. fin du billet).</p>
  46. <h2>Initialisation</h2>
  47. <p>Commençons par installer django, j'ai décidé de mettre les sources dans <strong>~/web/</strong> car j'avais l'habitude d'avoir la dernière version à jour et c'est quand même plus pratique d'être dans sa home pour faire un svn up. Depuis la sortie de la 0.96, la version du dépôt officiel n'est plus aussi stable qu'avant (comme il était prévu), nous allons donc installer la version 0.96 pour la refonte&nbsp;:</p>
  48. <pre>$ cd &amp; cd web
  49. $ wget http://www.djangoproject.com/download/0.96/tarball/
  50. $ tar xvf Django-0.96.tar.gz &amp;&amp; rm Django-0.96.tar.gz
  51. $ sudo ln -sf /home/david/web/Django-0.96/django /usr/lib/python2.5/site-packages/</pre>
  52. <p>À adapter bien entendu à votre configuration (python 2.5 par défaut avec Feisty \o/). Normalement la commande <strong>ls -l /usr/lib/python2.5/site-packages/dj*</strong> devrait vous renvoyer&nbsp;:</p>
  53. <pre>lrwxrwxrwx 1 root root 34 2007-04-02 08:32 /usr/lib/python2.5/site-packages/django -&gt; /home/david/web/Django-0.96/django</pre>
  54. <p>Vous pouvez aussi vérifier en faisant un <strong>python -c "import django;print django.VERSION"</strong> qui ne devrait pas lever d'erreur et renvoyer <strong>(0, 96, None)</strong>. <em>Si vous récupérez directement les sources du projet, les étapes d'initialisation suivantes sont inutiles.</em></p>
  55. <p>Ok, tout marche bien, créons donc notre projet de refonte (par commodité, je me mets dans le dossier refonte)&nbsp;:</p>
  56. <pre>$ mkdir refonte &amp;&amp; cd refonte
  57. $ /usr/lib/python2.5/site-packages/django/bin/django-admin.py startproject biologeek
  58. $ cd biologeek/</pre>
  59. <p>Vous pouvez aussi mettre le répertoire <strong>bin</strong> de <strong>django</strong> dans votre <strong>$PATH</strong> pour ne pas avoir à aller le récupérer dans les paquets python mais comme je ne m'en sers que pour la création de projets je trouve ça un peu inutile.</p>
  60. <p>Il ne reste plus qu'à paramétrer tout ça en modifiant le fichier <strong>settings.py</strong> et vous pourrez alors synchroniser la base pour la première fois&nbsp;:</p>
  61. <pre>$ python manage.py syncdb</pre>
  62. <p>La création d'un super-utilisateur est demandée, une fois cet utilisateur créé, vous pouvez lancer votre serveur de développement pour la première fois&nbsp;:</p>
  63. <pre>$ python manage.py runserver</pre>
  64. <p>En vous rendant sur http://127.0.0.1:8000/ vous aurez le message de bienvenue et sur /admin/ vous pourrez accéder à votre interface d'administration (en décommentant la ligne dans urls.py).</p>
  65. <p>On va maintenant créer les deux «&nbsp;applications » de la refonte&nbsp;: <strong>journal</strong> et <strong>bistrot</strong>.</p>
  66. <pre>$ python manage.py startapp journal
  67. $ python manage.py startapp bistrot</pre>
  68. <p>Je m'arrête là pour l'initiation à Django, ces bases là suffisent pour comprendre ce qui va suivre, si vous voulez en savoir plus <a href="https://larlet.fr/david/biologeek/archives/20060617-traduction-francaise-de-la-documentation-de-django-le-framework-web-python/">rien ne vaut un bon tutoriel</a>.</p>
  69. <h2>Modèles de données</h2>
  70. <p>Le modèle de données doit être placé dans les fichiers <strong>models.py</strong> situés dans les répertoires des deux applications créées. Le journal va contenir des billets qui auront le modèle suivant&nbsp;:</p>
  71. <pre>class Post(models.Model):
  72. title = models.CharField('Titre', maxlength=200)
  73. slug = models.SlugField('Adresse', prepopulate_from=('title',), primary_key='True')
  74. tags = models.ManyToManyField(Tag, help_text='Sélectionnez les tags.')
  75. summary = models.TextField('Description', help_text='Différentes syntaxes possibles (cf. markup)', blank=True)
  76. summary_html = models.TextField('Description HTML', blank=True)
  77. content = models.TextField('Contenu', help_text='Difféntes syntaxes possibles (cf. markup)')
  78. content_html = models.TextField('Contenu HTML', blank=True)
  79. related_posts = models.ManyToManyField('self', help_text='Sélectionnez les billets en relation.', blank=True)
  80. related = models.TextField('Contenu additionnel', help_text='Difféntes syntaxes possibles (cf. markup)', blank=True)
  81. related_html = models.TextField('Contenu additionnel HTML', blank=True)
  82. creation_date = models.DateTimeField('Date de création')
  83. modification_date = models.DateTimeField('Date de modification')
  84. publication_date = models.DateTimeField('Date de publication', null=True, blank=True)
  85. image = models.ImageField('Image associée', upload_to='%s/media/logos/' % settings.PROJECT_PATH, blank=True)
  86. is_draft = models.BooleanField('Brouillon', default=True)
  87. is_bestof = models.BooleanField('Best of', default=False)
  88. markup = models.CharField(maxlength=20, choices=MARKUP_CHOICES, radio_admin=True, default='markdown')</pre>
  89. <p>Je ne vais pas détailler, les champs parlent d'eux-même. Par contre, il est intéressant de commenter le manager que j'ai associé&nbsp;:</p>
  90. <pre>class PostManager(models.Manager):
  91. def get_query_set(self):
  92. """ Retrieve only published posts. """
  93. qs = super(PostManager, self).get_query_set()
  94. return qs.filter(is_draft=False)
  95. def get_best_of(self):
  96. """ Retrieve only best of published posts. """
  97. qs = super(PostManager, self).get_query_set()
  98. return qs.filter(is_bestof=True)</pre>
  99. <p>Un manager permet d'accéder facilement à un catégorie de billets, il faut pour cela ajouter à votre classe Post par exemple&nbsp;:</p>
  100. <pre>class Post(models.Model):
  101. [...]
  102. objects = models.Manager()
  103. published = PostManager()</pre>
  104. <p>Et la récupération des données est simplifiée ainsi&nbsp;:</p>
  105. <ul>
  106. <li><strong>Post.objects.all()</strong> pour l'ensemble des données&nbsp;;</li>
  107. <li><strong>Post.published.all()</strong> pour l'ensemble des données publiées&nbsp;;</li>
  108. <li><strong>Post.published.get_best_of()</strong> pour l'ensemble des données publiées et sélectionnées.</li>
  109. </ul>
  110. <p>On peut faire beaucoup de choses avec les managers et c'est relativement pratique pour ne pas se tromper lorsque l'on doit continuellement filtrer des données à accès restreint.</p>
  111. <p>Pour les brèves du bistrot, le code est assez similaire&nbsp;:</p>
  112. <pre>class Thought(models.Model):
  113. title = models.CharField('Titre', maxlength=200)
  114. slug = models.SlugField('Adresse', prepopulate_from=('title',))
  115. tags = models.ManyToManyField(Tag, help_text='Sélectionnez les tags.')
  116. content = models.TextField('Contenu', help_text='Vous pouvez utiliser markdown')
  117. content_html = models.TextField('Contenu HTML', blank=True)
  118. creation_date = models.DateTimeField('Date de création')
  119. modification_date = models.DateTimeField('Date de modification')
  120. publication_date = models.DateTimeField('Date de publication', null=True, blank=True)</pre>
  121. <p>Il y a juste le manager dans lequel je suis en train d'intégrer les votes donc j'en parlerais probablement plus tard, je vais m'arrêter là sinon je publierais jamais ce billet :-).</p>
  122. <h2>Accès au code source</h2>
  123. <p>J'ai créé pour l'occasion un <a href="http://code.google.com/p/biologeek/">projet sur Google Code qui contient l'intégralité du code</a>. Vous pouvez facilement récupérer le code avec subversion&nbsp;:</p>
  124. <pre>svn checkout http://biologeek.googlecode.com/svn/trunk/ biologeek</pre>
  125. <p>Checkoutez, testez, participez&nbsp;!</p>
  126. </div>
  127. </article>
  128. <footer>
  129. <h6 property="schema:datePublished">— 12/04/2007</h6>
  130. </footer>
  131. </section>
  132. <section>
  133. <div>
  134. <h3>Articles peut-être en rapport</h3>
  135. <ul>
  136. <li><a href="/david/biologeek/archives/20080423-biologeek-enfin-propulse-par-django/" title="Accès à Biologeek (enfin) propulsé par Django">Biologeek (enfin) propulsé par Django</a></li>
  137. <li><a href="/david/biologeek/archives/20070623-ajout-des-flux-rss-du-sitemap-et-des-commentaires-avec-django/" title="Accès à Ajout des flux RSS, du sitemap et des commentaires avec Django">Ajout des flux RSS, du sitemap et des commentaires avec Django</a></li>
  138. <li><a href="/david/biologeek/archives/20070523-de-dotclear-a-django-migration-des-donnees-et-redirections/" title="Accès à De Dotclear à Django : migration des données et redirections">De Dotclear à Django : migration des données et redirections</a></li>
  139. </ul>
  140. </div>
  141. </section>
  142. <section>
  143. <div id="comments">
  144. <h3>Commentaires</h3>
  145. <div class="comment" typeof="schema:UserComments">
  146. <p class="comment-meta">
  147. <span class="comment-author" property="schema:creator">NiCoS</span> le <span class="comment-date" property="schema:commentTime">12/04/2007</span> :
  148. </p>
  149. <div class="comment-content" property="schema:commentText">
  150. <p>Pour django-admin.py, tu peux aussi faire un simple lien symbolique :<br />
  151. sudo ln -s /usr/lib/python2.5/site-packages/django/bin/django-admin.py /usr/local/bin/django-admin.py <br />
  152. <br />
  153. Sinon si tu utilises Eclipse, PyDev ne suit pas les liens symboliques, dont tu dois ajouter /home/david/web/Django-0.96/ à ton PythonPath d'eclipse. Histoire d'avoir l'autocomplétion notamment :-)<br />
  154. <br />
  155. A quoi te servent les champ *_html ?<br />
  156. <br />
  157. Sinon intéressant le coup des manager, je connaissais pas, tout comme plein de petites choses que tu as mis à droite et à gauche :-)</p>
  158. </div>
  159. </div>
  160. <div class="comment" typeof="schema:UserComments">
  161. <p class="comment-meta">
  162. <span class="comment-author" property="schema:creator">David, biologeek</span> le <span class="comment-date" property="schema:commentTime">12/04/2007</span> :
  163. </p>
  164. <div class="comment-content" property="schema:commentText">
  165. <p>&gt; Pour django-admin.py, tu peux aussi faire un simple lien symbolique<br />
  166. Tout à fait, mais bon comme je disais ça sert pas tous les jours.<br />
  167. <br />
  168. &gt; Sinon si tu utilises Eclipse<br />
  169. Ma ram a généralement une meilleure utilité ;-).<br />
  170. <br />
  171. &gt; A quoi te servent les champ *_html ?<br />
  172. Je convertis selon le markup le contenu en html et je le stocke en base pour ne pas que la conversion soit faite à la volée à chaque chargement de page dans le template.<br />
  173. </p>
  174. </div>
  175. </div>
  176. <div class="comment" typeof="schema:UserComments">
  177. <p class="comment-meta">
  178. <span class="comment-author" property="schema:creator">NiCoS</span> le <span class="comment-date" property="schema:commentTime">12/04/2007</span> :
  179. </p>
  180. <div class="comment-content" property="schema:commentText">
  181. <p>Hmm pour les champ *_html, quel est le gain par rapport au fait d'utiliser le cache de django par ex ?</p>
  182. </div>
  183. </div>
  184. <div class="comment" typeof="schema:UserComments">
  185. <p class="comment-meta">
  186. <span class="comment-author" property="schema:creator">vincent</span> le <span class="comment-date" property="schema:commentTime">12/04/2007</span> :
  187. </p>
  188. <div class="comment-content" property="schema:commentText">
  189. <p>Pour le coup des managers, quand j'en fait un (notament pour iki), je faisais une QuerySet avec à tout les coups, pour pouvoir enchainer les différents critères (vu que c'est des objets QuerySet).<br />
  190. <br />
  191. Et j'utilise souvent le même Manager pour objects et `published`, avec un argument pris en compte dans le __init__(). Et les propriétés peuvent être fun aussi avec les managers :P<br />
  192. <br />
  193. Sinon, QdB :D Je vais m'en inspiré pour shortbrain (surtout le coup des *_html)</p>
  194. </div>
  195. </div>
  196. <div class="comment" typeof="schema:UserComments">
  197. <p class="comment-meta">
  198. <span class="comment-author" property="schema:creator">cédric</span> le <span class="comment-date" property="schema:commentTime">18/06/2009</span> :
  199. </p>
  200. <div class="comment-content" property="schema:commentText">
  201. <p>Je mets mon commentaire ici parce que c&#39;est le seul qui fait référence au champ ImageField.</p>
  202. <p>J&#39;ai déclaré dans une classe un champ tel que :<br />picture = ImageField(upload_to=&#39;.&#39;)</p>
  203. <p>Quand j&#39;utilise l&#39;interface admin<br />j&#39;arrive à charger mon image et à la sauvegarder<br />dans le dossier MEDIA_ROOT</p>
  204. <p>Quand je clique sur le lien proposé par l&#39;interface admin, j&#39;ai l&#39;impression que Django ne réutilise pas MEDIA_ROOT mais cherche dans le dossier admin des sources django, pourquoi ?</p>
  205. </div>
  206. </div>
  207. <div class="comment" typeof="schema:UserComments">
  208. <p class="comment-meta">
  209. <span class="comment-author" property="schema:creator">David, biologeek</span> le <span class="comment-date" property="schema:commentTime">18/06/2009</span> :
  210. </p>
  211. <div class="comment-content" property="schema:commentText">
  212. <p>@cédric : tu as pensé à spécifier le setting MEDIA_URL ?</p>
  213. <p><a href="http://docs.djangoproject.com/en/dev/ref/settings/#media-url">http://docs.djangoproject.com/en/dev/ref/settings/#media-url</a></p>
  214. </div>
  215. </div>
  216. <div class="comment" typeof="schema:UserComments">
  217. <p class="comment-meta">
  218. <span class="comment-author" property="schema:creator">cedric</span> le <span class="comment-date" property="schema:commentTime">19/06/2009</span> :
  219. </p>
  220. <div class="comment-content" property="schema:commentText">
  221. <p>oui, justement il va chercher dans :<br />/usr/lib/python2.6/dist-packages/django/contrib/admin/media/P1060799.JPG</p>
  222. <p>il ne tiens pas compte de MEDIA_URL :<br />MEDIA_URL = &#39;http://127.0.0.1:8000/media/&#39;</p>
  223. <p>mais de AMIN_MEDIA_PREFIX :<br />ADMIN_MEDIA_PREFIX = &#39;/media/&#39;</p>
  224. <p>si ADMIN_MEDIA_PREFIX = &#39;/media/toto/&#39;<br />alors il cherchera à l&#39;adresse : /usr/lib/python2.6/dist-packages/django/contrib/admin/media/toto/P1060799.JPG</p>
  225. <p>rappel : j&#39;essaie déjà via l&#39;interface admin</p>
  226. </div>
  227. </div>
  228. <div class="comment" typeof="schema:UserComments">
  229. <p class="comment-meta">
  230. <span class="comment-author" property="schema:creator">cédric</span> le <span class="comment-date" property="schema:commentTime">19/06/2009</span> :
  231. </p>
  232. <div class="comment-content" property="schema:commentText">
  233. <p>oui, justement il va chercher dans :<br />/usr/lib/python2.6/dist-packages/django/contrib/admin/media/P1060799.JPG</p>
  234. <p>il ne tiens pas compte de MEDIA_URL :<br />MEDIA_URL = &#39;http://127.0.0.1:8000/media/&#39;</p>
  235. <p>mais de AMIN_MEDIA_PREFIX :<br />ADMIN_MEDIA_PREFIX = &#39;/media/&#39;</p>
  236. <p>si ADMIN_MEDIA_PREFIX = &#39;/media/toto/&#39;<br />alors il cherchera à l&#39;adresse : /usr/lib/python2.6/dist-packages/django/contrib/admin/media/toto/P1060799.JPG</p>
  237. <p>rappel : j&#39;essaie déjà via l&#39;interface admin</p>
  238. </div>
  239. </div>
  240. <div class="comment" typeof="schema:UserComments">
  241. <p class="comment-meta">
  242. <span class="comment-author" property="schema:creator">David, biologeek</span> le <span class="comment-date" property="schema:commentTime">20/06/2009</span> :
  243. </p>
  244. <div class="comment-content" property="schema:commentText">
  245. <p>Il doit y avoir conflit entre ADMIN_MEDIA_PREFIX et MEDIA_URL.</p>
  246. <p>Je te conseille de tester :<br />MEDIA_URL = &#39;/media/&#39;<br />ADMIN_MEDIA_PREFIX = &#39;/admin_media/&#39;</p>
  247. <p>Et si ça marche pas de passer par la liste django francophone, ça sera plus approprié et il y a plus de monde ;)<br /><a href="http://lists.afpy.org/mailman/listinfo/django">http://lists.afpy.org/mailman/listinfo/django</a></p>
  248. </div>
  249. </div>
  250. </div>
  251. </section>
  252. <footer>
  253. <nav>
  254. <p>
  255. <small>
  256. 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>
  257. </small>
  258. </p>
  259. </nav>
  260. </footer>
  261. </div>
  262. <script src="/static/david/js/larlet-david-3ee43f.js" data-no-instant></script>
  263. <script data-no-instant>InstantClick.init()</script>
  264. </body>
  265. </html>