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.
  • You can hook into the CSV-generation API by passing response as the first argument to csv.writer. The csv.writer function expects a file-like object, and HttpResponse objects fit the bill.
  • Para cada linha no seu arquivo CSV, chamar o método writer.writerow, passando um iterable.
  • The CSV module takes care of quoting for you, so you don’t have to worry about escaping strings with quotes or commas in them. Pass writerow() your raw strings, and it’ll do the right thing.

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 %}

This short template iterates over the given data and displays a line of CSV for each row. It uses the addslashes template filter to ensure there aren’t any problems with quotes.

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.

Back to Top