title:      Ajout des flux RSS, du sitemap et des commentaires avec Django
slug:       ajout-des-flux-rss-du-sitemap-et-des-commentaires-avec-django
date:       2007-06-23 19:21:39
type:       post
vignette:   images/logos/biologeek.png
contextual_title1:  Biologeek (enfin) propulsé par Django
contextual_url1:    20080423-biologeek-enfin-propulse-par-django
contextual_title2:  De Dotclear à Django : migration des données et redirections
contextual_url2:    20070523-de-dotclear-a-django-migration-des-donnees-et-redirections
contextual_title3:  Vues génériques, héritage et templatetags : développez rapidement avec Django
contextual_url3:    20070424-vues-generiques-heritage-et-templatetags-developpez-rapidement-avec-django

<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 commence vraiment à trainer en longueur... il faut dire que ça me prend souvent plus de temps de décrire ce qui est fait que de le coder&nbsp;! Du coup c'est probablement le dernier billet à ce sujet. De toute façon c'est presque terminé, il ne me reste plus qu'à mettre quelques surprises et à adapter le thème, voire peut-être à tenter un <a href="http://forum.alsacreations.com/concours/cssr2007/">Cascading Style Summer Refresh 2007</a> mais j'en doute car je vais manquer de temps ces prochains mois.</p>

<h2>Ajout des flux RSS et Atom</h2>


<p>J'essaye toujours d'<strong>utiliser au maximum les fonctionnalités offertes par Django</strong> qui suit la philosophie Python «&nbsp;batteries incluses ». Dans le cas des flux je voulais quelque chose de très spécifique donc il a fallu mettre un peu les mains dans le cambouis mais c'est ce qui fait le charme d'un framework&nbsp;: <strong>on peut tout contrôler</strong>&nbsp;!</p>


<p>On commence par le cahier des charges, le but était d'avoir des URL du type <strong>/abonnement/{ rss, atom }/{journal, bistrot, tag, liste de tags }/</strong> afin de laisser la possibilité d'utiliser soit RSS, soit Atom tout en ayant des URL relativement courtes et compréhensibles. Au niveau du fichier des URL on retrouve donc&nbsp;:</p>


<pre>from biologeek.feeds import RSSFeed, AtomFeed

urlpatterns += patterns('django.contrib.syndication.views',
    (r'^abonnement/(?P&lt;url&gt;.*)/$', 'feed',
        {'feed_dict': { 'rss': RSSFeed, 'atom': AtomFeed }}
    ),
)</pre>

<p>Les clés du dictionnaire <strong>feed_dict</strong> vont être passées en paramètre de la fonction feed pour se retrouver dans l'URL donc jusqu'ici on a <strong>/abonnement/{ rss, atom }/</strong>. C'est ensuite au niveau des classes RSSFeed et AtomFeed qu'il faut décider du contenu qui va être proposé&nbsp;:</p>


<pre>from django.contrib.syndication.feeds import Feed
from biologeek.journal.models import Post

class RSSFeed(Feed):
    description = u'Dernières mises à jour du site de David Larlet : biologeek.com relatives aux %s'
    author_name = 'David Larlet'
    author_link = 'http://larlet.fr/'
    copyright   = 'Copyright (c) 2004-2007, David Larlet, Licence Art Libre'

    def get_object(self, bits):
        if len(bits) == 1:
            bit = bits[0]
            if bit == 'journal':
                self.title = 'Flux RSS des billets du journal du site biologeek.com'
                self.link = '/journal/'
                self.description = self.description % u'billets du journal'
                return Post.published.all()[:25]
            
            [...]
            
            else:
                raise ObjectDoesNotExist

    def items(self, obj):
        return obj</pre>

<p>Je ne mets pas tout mais vous aurez compris le principe (si non <a href="http://code.google.com/p/biologeek/">vous pouvez récupérer les sources sur le dépôt</a>). En fonction de la valeur passée en URL, un contenu approprié est proposé. C'est pas vraiment élégant et cela tient au cahier des charges un peu spécial que je m'étais fixé, sinon c'est beaucoup plus simple comme vous pouvez le voir <a href="http://www.djangoproject.com/documentation/syndication_feeds/">dans la documentation</a>.</p>


<p>Lorsque l'on veut passer au flux Atom, il suffit d'hériter de la classe utilisée pour le RSS&nbsp;:</p>


<pre>from django.utils.feedgenerator import Atom1Feed

class AtomFeed(RSSFeed):
    feed_type = Atom1Feed
    subtitle = RSSFeed.description</pre>


<h2>Ajout d'un sitemap</h2>


<p><strong>Un <a href="http://www.sitemaps.org/fr/">sitemap</a> est une aide pour les moteurs de recherche</strong>. C'est un fichier xml permettant d'indiquer l'importance relative des pages du site et leur fréquence de mise à jour. Il est généralement placé sur <strong>/sitemap.xml</strong> et il est nécessaire de <a href="http://www.google.com/webmasters/sitemaps/?hl=fr">le déclarer pour qu'il soit indexé</a>. On va ici se servir du <a href="http://www.djangoproject.com/documentation/sitemaps/">module sitemap de Django</a>, on rajoute donc l'URL&nbsp;:</p>


<pre>from biologeek.sitemaps import sitemaps

urlpatterns += patterns('django.contrib.sitemaps.views',
    (r'^sitemap.xml$', 'sitemap', {'sitemaps': sitemaps})
)</pre>

<p>La gestion des sitemaps peut se faire de manière générique, ce qui est très pratique pour les modèles que j'ai choisi&nbsp;:</p>


<pre>from django.contrib.sitemaps import GenericSitemap
from biologeek.journal.models import Post

sitemaps = {
    'journal_bestof': GenericSitemap(
        {
            'queryset': Post.published.filter(is_bestof=True),
            'date_field': 'publication_date'
        },
        priority=0.6
    ),
    'journal': GenericSitemap(
        {
            'queryset': Post.published.exclude(is_bestof=False),
            'date_field': 'publication_date'
        },
        priority=0.4
    ),
    [...]
}</pre>

<p>Par contre j'ai pas mal cherché pour arriver à rajouter les liens qui ne sont pas inhérents à des modèles comme l'accueil ou les pages d'agrégation de ressources. J'ai finalement créé une classe dédiée à ça&nbsp;:</p>


<pre>from django.contrib.sitemaps import Sitemap

class BaseSitemap(Sitemap):
    priority = 0.8

    def items(self):
        return ['/', '/journal/', '/bistrot/', '/archives/', '/abonnement/', '/contact/']

    def location(self, obj):
        return obj</pre>


<p>qu'il suffit ensuite d'ajouter au dictionnaire précédent. Comme vous pouvez le remarquer, je ne spécifie pas la fréquence de mise à jour car les pages de ressources sont susceptibles d'évoluer au cours du temps grâce aux commentaires et celles d'agrégations sont mises à jour de façon non régulière compte tenu de mon rythme de publication. <strong>Dans le doute, il vaut mieux s'abstenir Google saura mieux algorithmer tout ça que vous.</strong></p>


<h2>Ajout des commentaires</h2>


<p>Comme je le disais plus haut j'aime bien utiliser les modules déjà inclus dans Django. Pour les commentaires, <strong>je vais peut-être faire une petite exception pour deux raisons</strong>&nbsp;:</p>

<ul>
<li>même s'il est très puissant (peut-être trop d'ailleurs pour mes besoins&nbsp;: karma, etc), il est <strong>très peu extensible</strong> en l'état et doit être réécrit pour ça&nbsp;;</li>
<li>il utilise les <strong>anciens formulaires</strong> et je ne trouve pas intéressant de continuer à les utiliser aujourd'hui.</li>
</ul>

<p>Je n'ai pas encore décidé de ce que j'allais faire. Il me reste aussi à intégrer l'<a href="https://larlet.fr/david/biologeek/archives/20070104-comment-utiliser-openid-la-solution-d-identification-tant-attendue/">identification via OpenID</a> pour vous rendre la vie plus facile :-).</p>


<p>Prochaine étape... euh... <strong>la mise en ligne</strong>&nbsp;! J'ai pas mal galéré pour <a href="http://www.django-fr.org/">Django-fr</a> donc ça devrait aller beaucoup plus vite.</p>
