Ajout des flux RSS, du sitemap et des commentaires avec Django

vignette

Suite de la refonte de ce blog 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 ! 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 Cascading Style Summer Refresh 2007 mais j'en doute car je vais manquer de temps ces prochains mois.

Ajout des flux RSS et Atom

J'essaye toujours d'utiliser au maximum les fonctionnalités offertes par Django qui suit la philosophie Python « 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 : on peut tout contrôler !

On commence par le cahier des charges, le but était d'avoir des URL du type /abonnement/{ rss, atom }/{journal, bistrot, tag, liste de tags }/ 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 :

from biologeek.feeds import RSSFeed, AtomFeed

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

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

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

Je ne mets pas tout mais vous aurez compris le principe (si non vous pouvez récupérer les sources sur le dépôt). 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 dans la documentation.

Lorsque l'on veut passer au flux Atom, il suffit d'hériter de la classe utilisée pour le RSS :

from django.utils.feedgenerator import Atom1Feed

class AtomFeed(RSSFeed):
    feed_type = Atom1Feed
    subtitle = RSSFeed.description

Ajout d'un sitemap

Un sitemap est une aide pour les moteurs de recherche. 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 /sitemap.xml et il est nécessaire de le déclarer pour qu'il soit indexé. On va ici se servir du module sitemap de Django, on rajoute donc l'URL :

from biologeek.sitemaps import sitemaps

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

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 :

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
    ),
    [...]
}

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 :

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

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. Dans le doute, il vaut mieux s'abstenir Google saura mieux algorithmer tout ça que vous.

Ajout des commentaires

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

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

Je n'ai pas encore décidé de ce que j'allais faire. Il me reste aussi à intégrer l'identification via OpenID pour vous rendre la vie plus facile :-).

Prochaine étape... euh... la mise en ligne ! J'ai pas mal galéré pour Django-fr donc ça devrait aller beaucoup plus vite.

— 23/06/2007

Articles peut-être en rapport

Commentaires

zyegfryed le 23/06/2007 :

Salut,
Excellent article, dont la collection devrait figurer quelque part sur django-fr.org ! (Cela n'engage que moi, bien sur :))
Je pense avoir remarqué une coquille dans le code mentionné sur l'article (je n'ai par contre pas vérifié les sources, tu me pardonneras), au niveau de la définition des éléments du sitemap :

'journal': GenericSitemap(
{
'queryset': Post.published.exclude(is_bestof=False),
'date_field': 'publication_date'
},
priority=0.4
),

Il me semble que la valeur de is_bestof doit être à True, puisque l'on cherche à exclure les billets BestOf qui possèdent déja leur propre référence, non ?

Sinon, j'avais vu dans ton code source au niveau du modèle de billet (class Post il me semble) que tu utilisais un conteneur pour chaque élément formaté (Textile, Markdown, Dotclear, etc...). Ne serait il pas plus "convenable" de n'utiliser qu'un seul champ contenant le texte formaté, à l'aide du snippet Generic markup converter (www.djangosnippets.org/sn...) de James Bennett (ubernostrum) ? (Modulo adaptation afin que la variable markup_func_name ne se base plus que sur une variable de configuration, mais puisse être surchargée via les paramètres kwargs par exemple ou un attribut de classe).
Bye :)
Seb

David, biologeek le 23/06/2007 :

Salut Seb,

> Excellent article, dont la collection devrait figurer quelque part sur django-fr.org !

J'y penserais, j'attends d'avoir une catégorie Django bien définie pour poster le lien vers celle-ci.

> Il me semble que la valeur de is_bestof doit être à True, puisque l'on cherche à exclure les billets BestOf qui possèdent déja leur propre référence, non ?

Tout à fait, en fait le billet était déjà corrigé mais le flux RSS était déjà parti un peu partout, merci de l'avoir signalé ;-).

Concernant le snippet il était dans mes favoris mais je l'avais complètement oublié ! Je vais voir ce que je peux faire avec. Il faudra que j'intègre wiki2xhtml aussi...

Soso le 25/06/2007 :

Quand même, ça a l'air compliqué...
Merci en tout cas pour m'avoir "tuyauté" via ton exemple à l'utilité d'un sitemap pour mon blog. J'ai effectivement trouvé des générateurs de sitemap pour wordpress, je vais aller voir si ça existe pour Dotclear...

Bon courage !

David, biologeek le 25/06/2007 :

Il y a le plugin gsitemap pour dotclear disponible ici : callmepep.org/bricoland/

Merci pour les encouragements :-).

zyegfryed le 25/06/2007 :

Concernant les commentaires, James Bennett (www.b-list.org/) vient de sortir une application de modération "bien propre" (comparée aux hacks qu'il donnait il y a un an, d'après l'auteur) : comment_utils (code.google.com/p/django-...)
Ce pourrait être un petit plus pour la gestion des commentaires (surtout niveau modération du spam, etc.)...
Bye :)
Seb

David le 27/06/2007 :

Django-fr launched this past week. The site’s creator, David Larlet, posted the announcement to django-users: “The aim of this site is to provide translations of the documentation, useful links and of course to create a local community!” The site is still evolving as more users are becoming aware of it and contributing translations that will go through an approval process. The Django-fr group also coordinates their efforts on their Django-fr mailing list and on #django-fr on irc.freenode.net.

www.djangoproject.com/web...

Le début de la gloire ? ;-)

David, biologeek le 27/06/2007 :

@zyegfryed : oui j'ai vu, merci pour le lien. En fait c'est en train d'être réécrit si j'en crois Jacob groups.google.com/group/d...

Donc j'attends encore un peu mais en terme d'anti-spam c'est intéressant en effet.

@David : la gloire, la gloire, appelons ça plutôt la reconnaissance d'une équipe motivée :-).

NiCoS le 09/07/2007 :

Merci pour cet article nickel et qui me servira un jour pour Atome :-P

Plus je vois les contrib qui sortent pour Django, plus ça me pousse à retarder le dev d'Atome parfois :-D

On va déjà finaliser et déployer la version 0.1 de MvMo et on verra bien ensuite dans quoi je vais me lancer (ou pas).

++