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 acsv.writer
. La funzionecsv.writer
si aspetta un oggetto file-like eHttpResponse
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.