Come creare un output su CSV

Questo documento spiega come fare output di CSV (Comma Separated Values) in modo dinamico usando Django. Per fare ciò, puoi sia usare la libreria CSV di Python che il sistema di template di Django.

Utilizzando la libreria CSV di Python

Python è dotato di una libreria CSV, csv. La chiave dell’usarla con Django è che le capacità di creazione di CSV del modulo csv agiscono su oggetti file-like e gli oggetti di Django HttpResponse sono oggetti file-like.

Qui un esempio:

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",
        headers={"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

Il codice ed i commenti dovrebbero essere auto esplicativi, ma alcuni dettagli meritano un approfondimento:

  • La response ottiene un MIME type speciale, text/csv. Questo dice ai browser che il documento è un file CSV, piuttosto che un file HTML. Se non lo specifichi, i browser probabilmente intepreteranno l’output come HTML, che risulterà in un brutto e spaventoso nonsense nella finestra del browser.
  • La risposta ottine un header Content-Disposition aggiuntivo, che contiene il nome del file CSV. Questo nome è arbitrario; chiamalo come vuoi. Sarà usato dai browser nel dialogo «Salva come…» ecc…
  • Puoi agganciarti alla API per la generazione di CSV passando response come primo argomento a csv.writer. La funzione csv.writer si aspetta un oggetto file-like e HttpResponse fa al caso suo.
  • Per ogni riga del tuo CSV, chiama writer.writerow, passando un iterable.
  • Il modulo CSV si occupa del quoting per te, quindi non devi preoccuparti di fare escape delle stringhe con quote o virgole. Passa a writerow() le tue stringhe grezze e farà la cosa giusta.

Streaming di grossi file CSV

Quando hai a che fare con view che generano response molto grandi, potresti voler considerare StreamingHttpResponse di Django, invece. Per esempio, facendo streaming di un file che ci mette tanto ad essere generato puoi evitare che il load balancer interrompa una connessione che sarebbe andata fuori tempo massimo mentre il server generava la risposta.

In questo esempio, utilizziamo i generatori Python per gestire in modo efficiente la composizione e la trasmissione di un file CSV di grandi dimensioni:

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)
    return StreamingHttpResponse(
        (writer.writerow(row) for row in rows),
        content_type="text/csv",
        headers={"Content-Disposition": 'attachment; filename="somefilename.csv"'},
    )

Utilizzare il template system

Alternativamente, puoi usare il sistema di template di Django per generare CSV. Questo è più low-level rispetto all’utilizzo del conveniente modulo csv ma la soluzione viene qui proposta per completezza.

L’idea è di passare una lista di oggetti al tuo template, e lasciare che il template crei le virgole nel ciclo for

Qui c’è un esempio, che genera lo stesso file CSV visto in precedenza:

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


def some_view(request):
    # Create the HttpResponse object with the appropriate CSV header.
    response = HttpResponse(
        content_type="text/csv",
        headers={"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 = {"data": csv_data}
    response.write(t.render(c))
    return response

La sola differenza tra questo esempio e quello precedente è che questo usa il caricamento del template invece del modulo CSV. Il resto del codice – come content_type='text/csv' – è lo stesso.

Quindi, crea il template my_template_name.txt, con questo codice:

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

Questo breve template itera i dati e mostra una riga del CSV alla volta. Utilizza il filtro template addslashes per essere sicuro che non ci siano problemi con gli apici.

Altri formati basati sul testo

Nota che non c’è niente di molto specifico di CSV qui – solo il formato di output. Puoi usare entrambe queste tecniche per fare output di qualsiasi file in formato testo che vuoi. Puoi anche utilizzare una tecnica simile per generare dati binari arbitrari; vedi Come creare file PDF per avere un esempio.

Back to Top