Генерация CSV на Django

Здесь описывается как генерировать CSV (Comma Separated Values) с помощью представлений Django. Для этого нам понадобится библиотека для работы с CSV или шаблоны Django.

Использование библиотеки для работы с CSV

Python поставляется с библиотекой csv. Фишка в том, что этот модуль работает с файловыми объектами, которым как раз и является HttpResponse.

Пример:

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

Код и комментарии должны быть понятны, но на некоторые вещи стоит обратить внимание:

  • Ответу устанавливается определённый тип text/csv, этим мы говорим браузеру, что передаём файл в формате CSV, а не HTML. Если этого не сделать, то браузер будет пытаться отобразить документ как HTML, что может испугать пользователя.
  • Ответ также имеет дополнительный заголовок Content-Disposition, в котором содержится имя файла. Оно будет подставлено в окне сохранения файла и других случаях.
  • 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.
  • For each row in your CSV file, call writer.writerow, passing it an 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.

Стриминг больших CSV файлов

При работе с представлениями, которые генерируют ответ с большим количеством данных, вам следует использовать StreamingHttpResponse. Стримя большой файл, можно избежать обрыва подключения по таймауту при использовании балансировщика нагрузки.

В этом примере мы используем генераторы Python для создания и передачи большого 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

Использование шаблонов

Однако, вы можете использовать Django template system для генерации CSV. Это низкоуровневое решение представлено здесь для полноты картины.

Суть в том, чтобы обработать список элементов вашим шаблоном и вывести в цикле с помощью, например, for.

Ниже пример реализации:

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

Единственное отличие между этим примером и предыдущим состоит в том, что используется шаблон Django, а не модуль csv. Всё остальное, например content_type='text/csv', совпадает.

Затем создайте шаблон my_template_name.txt со следующим кодом:

{% 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.

Остальные текстовые форматы

Обратите внимание, что специфики именно CSV здесь нет. Подобным образом можно работать с остальными форматами: как текстовыми, так и бинарными (например, Генерация PDF на Django).