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.

article.md 6.8KB

title: De Dotclear à Django : migration des données et redirections slug: de-dotclear-a-django-migration-des-donnees-et-redirections date: 2007-05-23 19:35:18 type: post vignette: images/logos/biologeek.png contextual_title1: Biologeek (enfin) propulsé par Django contextual_url1: 20080423-biologeek-enfin-propulse-par-django contextual_title2: Ajout des flux RSS, du sitemap et des commentaires avec Django contextual_url2: 20070623-ajout-des-flux-rss-du-sitemap-et-des-commentaires-avec-django 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

À force de me faire chambrer sur le retard de ma refonte, j'ai décidé de prendre le taureau par les cornes en attaquant la migration des données. Je me suis rendu compte que ce n'était finalement pas si difficile que ça avec Django...

Migration des données

La première étape est de récupérer les données depuis Dotclear. J'avais commencé à faire des scripts SQL mais j'ai finalement opté pour une solution en texte brut qui m'offrait plus de souplesse. J'ai donc installé le plugin flatExport pour Dotclear qui permet de récupérer le contenu du blog sous forme de fichier texte. Quelques lignes de python et l'on converti ce fichier en dictionnaires pour faciliter l'accès aux données (c'est un peu le relationnel du pauvre...) :

for table in open('blog-backup.txt').read().split('
',1)[1].strip().split('

'):
    header, content = table.split('
',1)
    table_name, column_names = header[1:-1].split(' ')
    column_names = tuple(column_names.split(','))
    blog[table_name] = {}
    for item in content.split('
'):
        column_contents = tuple((decode_html(column) for column in item[1:-1].split('","')))
        item_dict = dict(zip(column_names, column_contents))
        id = item_dict[column_names[0]]
        blog[table_name][id] = item_dict

La grande force de Django c'est de pouvoir utiliser votre projet dans un script python afin de pouvoir appeler directement les objets/classes/tables en python (ce sont ces petits détails qui font apprécier un framework plutôt qu'un autre), c'est ce que j'ai fait pour remplir mes nouvelles tables avec les données issues de Dotclear :

for post_dict in blog['post'].values():
    django_post = Post()
    django_post.title = post_dict['post_titre']
    django_post.slug = post_dict['post_titre_url']
    django_post.tags = blog['categorie'][post_dict['cat_id']]['cat_libelle_url'].lower()

    django_post.summary_html = post_dict['post_chapo']
    django_post.summary = post_dict['post_chapo_wiki']
    django_post.content_html = post_dict['post_content']
    django_post.content = post_dict['post_content_wiki']

    django_post.creation_date = post_dict['post_creadt']
    django_post.modification_date = post_dict['post_upddt']
    django_post.publication_date = post_dict['post_dt']

    django_post.is_online = post_dict['post_pub']
    django_post.is_bestof = post_dict['post_selected']
    django_post.markup = 'wiki2xhtml'

    django_post.save()

Et voila, je peux maintenant vérifier le nombre de billets présents avec le shell de Django :

$ python manage.py shell
>>> from biologeek.journal.models import Post
>>> Post.objects.count()
190

Parfait !

Le script complet est disponible sur le dépôt, il a fallu que je modifie un peu le plugin de Dotclear pour qu'il prenne en compte mes tables de blogmarks. Il ne me reste plus qu'à migrer les commentaires.

Redirections

Un autre point essentiel lors d'une refonte est de rediriger chaque ancienne URL vers la nouvelle et ce de manière permanente. Il existe une application de redirection dans les contributions de Django mais ça ne convenait pas à mes modifications au niveau des URL des billets qui nécessitent de récupérer leurs tags pour faire la redirection.

Heureusement, oh oui heureusement, que j'ai conservé le index.php dans toutes mes URL car sinon la complexité des expressions régulières à gérer m'aurait donné un mal de crâne interminable. Du coup, ça devient vraiment plus simple car je peux rediriger toutes les URL commençant par /journal/index.php vers mon script de redirection. Le script prend en compte toutes les URL que j'ai pu répertorié (j'espère ne pas en avoir oublié, au pire je les rajouterais lors de la migration) :

urlpatterns = patterns('',
    ('archives',                redirect('/archives/')),
    ('rss.php$',                redirect_rss('/rss/journal/')),
    ('atom.php$',               redirect_rss('/rss/journal/')),
    ('rss_ailleurs.xml$',       redirect('/rss/bistrot/')),
    ('(?P<tag>[A-Z][-\w]+)/$',  redirect_tag('/%(tag)s/')),
    ('(?P<slug>[-\w]+)/$',      redirect_post()),
    ('$',                       redirect('/journal/')),
)

[edit du lendemain] : finalement c'est pas si simple car certaines URL ne sont pas derrière index.php, du coup ça donne ça :

urlpatterns = patterns('',
    ('index.php/archives',                  redirect('/archives/')),
    ('index.php/(?P<tag>[A-Z][-\w]+)/$',    redirect_tag('/%(tag)s/')),
    ('index.php/(?P<slug>[-\w]+)/$',        redirect_post()),
    ('index.php/$',                         redirect('/journal/')),
    ('rss.php$',                            redirect_rss('/feeds/rss/')),
    ('atom.php$',                           redirect_rss('/feeds/rss/')),
    ('rss_ailleurs.xml$',                   redirect('/feeds/rss/bistrot/')),
)

La fonction appelée fait ensuite une redirection permanente (301) vers la nouvelle URL, par exemple dans le cas des billets :

def redirect_post():
    def inner(request, slug):
        post = Post.published.get(slug=slug)
        return HttpResponsePermanentRedirect(post.get_absolute_url())
    return inner

Au final il ne m'aura fallu que quelques lignes de python pour franchir cette nouvelle étape, la prochaine fois on ajoute les commentaires et les fils RSS. On avance, on avance...