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, chamar o método
writer.writerow
, passando um iterable. - 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.
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.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
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
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 = {'data': csv_data}
response.write(t.render(c))
return response
A única diferença entre este exemplo e o exemplo anterior é que este usa carga 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.