Emitindo CSV com Django¶
Este documento explica como emitir CSV (valores separados por vírgulas) dinamicamente usando “views” do Django. Para fazer isso, você pode usar a biblioteca CSV do Python ou o sistema de templates do Django.
Usando a biblioteca CSV do Python.¶
Python vem com uma biblioteca CSV, csv
. A chave para usá-lo com Django é que a capacidade CSV-creation do módulo csv
age como um objeto “file-like”, e aos objetos da class:~django.http.HttpResponse do Django são objetos “file-like”.
Aqui está um exemplo
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
O código e comentário devem ser auto-explanaveis, mas algumas coisas merecem uma menção.
A resposta recebe um tipo MIME especial text/csv. Isso diz ao browser que o documento é um arquivo CSV, ao invés de um arquivo HTML. Se deixar isso desligado, os browsers vão provavelmente interpretar a resposta como HTML, o qual irá resultar em uma feia e assustadora gobbledygook na janela do seu browser.
A resposta recebe um cabeçalho adicional
Content-Dispoition
, o qual contém o nome do arquivo CSV. Este nome de arquivo e arbitrário; chame isso do que você quiser. Ele será usado pelo browser no diálogo “Salvar como...”, etc.Utilizar a API do CSV-generation é fácil: Apenas passe
response``como o primeiro argumento para ``csv.writer
. A funçãocsv.writer
espera um objeto “file-like”, e o objetosHttpResponse
é um “file-loke”.Para cada linha no seu arquivo CSV, chame
writer.writerow
, passando-o para um objeto interável tal como uma lista ou tupla.O módulo CSV cuida das aspas pra você, então não tem que se preocupar em substituir strings com aspas ou vírgulas. Apenas passe as string para
writerow()
, e ele fará a coisa certa.
Manipulando unicode no Python 2
O móduo csv
do Python 2’s não suporta entrada unicode. E já que o Django usa Unicode internamente isso significa que strings lidas do código fonte tal como HttpRequest
são potencialmente problemáticas. Existem algumas opções para lidar com isso:
Manualmente encode todas os objetos Unicode para um encode compatível.
Use a class
UnicodeWriter
fornecida no csv module’s examples section.Use o python-unicodecsv module, o qual objetiva ser um substituto para o
csv
que normalmente lida com Unicode.
Para mais informação, veja a documentação Python do módulo csv
.
Transmitindo grandes arquivos CSV¶
Quando lida com “views” que geram respostas muito grandes, talvez queira considerar usar o StreamingHttpResponse
do Django no lugar. Por exemplo, por trasmitir em fluxo contínuo ou “streaming” um arquivo que leva muito tempo para ser gerado você pode evitar que a conexão seja perdida tentando balancear a carga que talvez expire enquanto o servidor estava gerando a resposta.
Neste exemplo, fazemos uso total de geradores Python para manipular eficientemente a montagem e transmissão de um grande arquivo CSV:
import csv
from django.utils.six.moves import range
from django.http import StreamingHttpResponse
class Echo(object):
"""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
Usando o sistema de template.¶
Uma alternatica, você pode usar o sistema de tempaltes do Django para gerar CSV. Isso é mais baixo nível que usar o conveniente módulo Python csv
, mas a solução é apresentada aqui para para abrangência.
A idéia aqui é passar uma lista de itens para seu template, e ter a saida do template as vírgulas em um loop for
.
Aqui um exemplo, o qual gera o mesmo arquivo CSV que acima:
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
A única diferença entre este exemplo e o exemplo anterior é que este usa carfa de template ao invés do módulo CSV. O resto do código – tal como content_type='text/csv'
– é o mesmo.
Então, crie o template my_template_name.txt
, com este código no template:
{% for row in data %}"{{ row.0|addslashes }}", "{{ row.1|addslashes }}", "{{ row.2|addslashes }}", "{{ row.3|addslashes }}", "{{ row.4|addslashes }}"
{% endfor %}
Este template é bem básico. Ele somente itera sobre o dado enviado e mostra uma linha de CSV para cada linha. Isso usa o filtro de template addslashes
para se assegurar que não haverá nenhum problema com aspas.
Outros formatos baseados em texto.¶
Note que não tem muita coisa específica sobre CSV aqui – apenas o formato de saída. Você pode usar qualquer uma destas técnicas para qualquer formato texto que você imagine. Você também pode usar essa técnica para gerar dados binários arbitrários; veja Gerando PDFs com o Django por exemplo.