Hur man skapar CSV-utdata¶
Detta dokument förklarar hur man dynamiskt skriver ut CSV (Comma Separated Values) med hjälp av Django-vyer. För att göra detta kan du antingen använda Pythons CSV-bibliotek eller Djangos mallsystem.
Använda Pythons CSV-bibliotek¶
Python levereras med ett CSV-bibliotek, csv
. Nyckeln till att använda det med Django är att csv
-modulens CSV-skapande kapacitet fungerar på filliknande objekt, och Djangos HttpResponse
-objekt är filliknande objekt.
Här är ett exempel:
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
Koden och kommentarerna borde vara självförklarande, men det finns några saker som förtjänar att nämnas:
Svaret får en speciell MIME-typ, text/csv. Detta talar om för webbläsarna att dokumentet är en CSV-fil, snarare än en HTML-fil. Om du utelämnar detta kommer webbläsarna förmodligen att tolka utdata som HTML, vilket resulterar i ful, skrämmande gobbledygook i webbläsarfönstret.
Svaret får ytterligare en
Content-Disposition
header, som innehåller namnet på CSV-filen. Detta filnamn är godtyckligt; kalla det vad du vill. Det kommer att användas av webbläsare i dialogrutan ”Spara som…” etc.Du kan koppla in dig på CSV-generering API genom att skicka
response
som det första argumentet tillcsv.writer
. Funktionencsv.writer
förväntar sig ett filliknande objekt, ochHttpResponse
-objekt passar bra.För varje rad i CSV-filen anropar du
writer.writerow
och ger den en iterable.CSV-modulen tar hand om citering åt dig, så du behöver inte oroa dig för att undkomma strängar med citattecken eller kommatecken i dem. Skicka dina råa strängar till
writerow()
så gör den det rätta.
Streaming av stora CSV-filer¶
När du hanterar vyer som genererar mycket stora svar kanske du vill överväga att använda Djangos StreamingHttpResponse
istället. Genom att strömma en fil som tar lång tid att generera kan du till exempel undvika att en lastbalanserare tappar en anslutning som annars skulle ha avbrutits medan servern genererade svaret.
I det här exemplet utnyttjar vi Python-generatorer för att effektivt hantera montering och överföring av en stor CSV-fil:
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"'},
)
Använda mallsystemet¶
Alternativt kan du använda Django mallsystem för att generera CSV. Detta är på en lägre nivå än att använda den praktiska Python csv
-modulen, men lösningen presenteras här för fullständighetens skull.
Tanken här är att skicka en lista med objekt till din mall och låta mallen mata ut kommatecken i en for
-loop.
Här är ett exempel som genererar samma CSV-fil som ovan:
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
Den enda skillnaden mellan det här exemplet och det föregående är att det här exemplet använder template loading i stället för CSV-modulen. Resten av koden - till exempel content_type='text/csv'
- är densamma.
Skapa sedan mallen my_template_name.txt
, med denna mallkod:
{% for row in data %}"{{ row.0|addslashes }}", "{{ row.1|addslashes }}", "{{ row.2|addslashes }}", "{{ row.3|addslashes }}", "{{ row.4|addslashes }}"
{% endfor %}
Denna korta mall itererar över de angivna data och visar en rad CSV för varje rad. Den använder mallfiltret addslashes
för att säkerställa att det inte finns några problem med citattecken.
Andra textbaserade format¶
Observera att det inte finns så mycket specifikt för CSV här - bara det specifika utdataformatet. Du kan använda någon av dessa tekniker för att mata ut vilket textbaserat format som helst som du kan drömma om. Du kan också använda en liknande teknik för att generera godtyckliga binära data; se Så här skapar du PDF-filer för ett exempel.