Production de CSV avec Django

Ce document explique comment produire dynamiquement du contenu CSV (Comma Separated Values) en utilisant des vues Django. Pour cela, vous pouvez soit utiliser la bibliothèque CSV de Django, soit le système de gabarits de Django.

La bibliothèque CSV de Python

Python contient une bibliothèque CSV, csv. L’élément clé dans son utilisation avec Django est que les fonctionnalités de création de CSV du module csv agissent sur des objets de type fichier, et les objets HttpResponse de Django sont justement des objets de type fichier.

Voici un exemple :

import csv
from django.http import HttpResponse

def some_view(request):
    # Create the HttpResponse object with the appropriate CSV header.
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'

    writer = csv.writer(response)
    writer.writerow(['First row', 'Foo', 'Bar', 'Baz'])
    writer.writerow(['Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"])

    return response

Le code et les commentaires sont assez explicites, mais certains points méritent un éclaircissement :

  • La réponse est initialisée avec un type MIME spécial, text/csv. Ceci indique aux navigateurs que le document est un fichier CSV, et non pas un fichier HTML. Si vous omettiez ce paramètre, les navigateurs interpréteraient probablement le contenu comme du HTML, ce qui résulterait en un affichage cryptique aux allures de code secret.
  • L’en-tête Content-Disposition de la réponse est aussi défini avec le nom du fichier CSV. Ce nom est totalement arbitraire, donnez-lui le nom que vous voulez. Il sera notamment utilisé par les navigateurs dans la boîte de dialogue « Enregistrer sous ».
  • Le branchement à l’API de génération de CSV est facile : il suffit de passer response comme premier paramètre à csv.writer. La fonction csv.writer s’attend à un objet de type fichier et HttpResponse joue très bien ce rôle.
  • Pour chaque ligne de votre fichier CSV, appelez writer.writerow en lui passant un objet itérable tel qu’une liste ou un tuple.
  • Le module CSV s’occupe des guillemets, vous n’avez donc pas besoin de vous soucier d’échapper les chaînes contenant des guillemets ou des virgules. Il suffit de passer vos chaînes brutes à writerow() et il s’occupera de faire ce qu’il faut.

Diffusion continue de fichiers CSV volumineux

Lorsque vous avez affaire à des vues qui génèrent des réponses très grandes, il faut considérer l’utilisation de réponses StreamingHttpResponse. Par exemple, avec la diffusion continue d’un fichier qui prend beaucoup de temps à générer, vous pouvez éviter la perte de connexion de l’équilibreur de charge qui, autrement, aurait expiré pendant que le serveur générait la réponse.

Dans cet exemple, nous utilisons pleinement les générateurs Python pour gérer efficacement l’assemblage et la transmission d’un fichier CSV volumineux :

import csv

from django.http import StreamingHttpResponse

class Echo:
    """An object that implements just the write method of the file-like
    interface.
    """
    def write(self, value):
        """Write the value by returning it, instead of storing in a buffer."""
        return value

def some_streaming_csv_view(request):
    """A view that streams a large CSV file."""
    # Generate a sequence of rows. The range is based on the maximum number of
    # rows that can be handled by a single sheet in most spreadsheet
    # applications.
    rows = (["Row {}".format(idx), str(idx)] for idx in range(65536))
    pseudo_buffer = Echo()
    writer = csv.writer(pseudo_buffer)
    response = StreamingHttpResponse((writer.writerow(row) for row in rows),
                                     content_type="text/csv")
    response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
    return response

Utilisation du système de gabarits

Vous avez aussi la possibilité d’utiliser le système de gabarits de Django pour produire le CSV. Cette technique est de plus bas niveau que l’utilisation du module Python csv, mais cette alternative est présentée ici par souci d’exhaustivité.

L’idée ici est de passer une liste d’éléments à un gabarit qui se chargera de générer les virgules dans une boucle for.

Voici un exemple qui génère le même fichier CSV que ci-dessus :

from django.http import HttpResponse
from django.template import loader, Context

def some_view(request):
    # Create the HttpResponse object with the appropriate CSV header.
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'

    # The data is hard-coded here, but you could load it from a database or
    # some other source.
    csv_data = (
        ('First row', 'Foo', 'Bar', 'Baz'),
        ('Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"),
    )

    t = loader.get_template('my_template_name.txt')
    c = Context({
        'data': csv_data,
    })
    response.write(t.render(c))
    return response

La seule différence entre cet exemple et le précédent est que celui-ci utilise le chargement d’un gabarit à la place du module CSV. Le reste du code (comme content_type='text/csv') est identique.

Puis, créez le gabarit my_template_name.txt avec ce contenu :

{% for row in data %}"{{ row.0|addslashes }}", "{{ row.1|addslashes }}", "{{ row.2|addslashes }}", "{{ row.3|addslashes }}", "{{ row.4|addslashes }}"
{% endfor %}

Le gabarit est très simple. Il ne fait que parcourir les données reçues et affiche une ligne de CSV pour chaque ligne. Il utilise le filtre de gabarit addslashes pour s’assurer qu’il n’y aura pas de problèmes avec les guillemets.

Autres formats textuels

Vous constaterez que ces exemples ne contiennent pas beaucoup de code spécifique au format CSV, uniquement le format de sortie spécifique. Vous pouvez donc employer l’une de ces techniques pour générer tout autre format basé sur du texte. Vous pouvez aussi utiliser des techniques semblables pour produire n’importe quel contenu binaire ; consultez Production de PDF avec Django pour voir un exemple.

Back to Top