Как создать выходной файл CSV

Этот документ объясняет, как динамически выводить CSV (Comma Separated Values) с помощью представлений Django. Для этого вы можете использовать либо библиотеку Python CSV, либо систему шаблонов Django.

Использование библиотеки Python CSV

Python поставляется с библиотекой CSV, csv. Ключ к ее использованию с Django заключается в том, что возможность создания CSV модулем csv действует на файлоподобные объекты, а объекты Django 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",
        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

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

  • Ответ получает специальный MIME-тип, text/csv. Это указывает браузерам, что документ является CSV-файлом, а не HTML-файлом. Если вы оставите этот параметр без внимания, браузеры, вероятно, будут интерпретировать вывод как HTML, что приведет к появлению уродливой, страшной тарабарщины в окне браузера.
  • В ответ приходит дополнительный заголовок Content-Disposition, который содержит имя файла CSV. Это имя файла произвольное, называйте его как хотите. Оно будет использоваться браузерами в диалоге «Сохранить как…» и т.д.
  • Вы можете подключиться к API генерации CSV, передав response в качестве первого аргумента в csv.writer. Функция csv.writer ожидает файл-подобный объект, и объекты HttpResponse подходят для этого.
  • Для каждой строки в вашем CSV-файле вызовите writer.writerow, передавая ей iterable.
  • Модуль CSV позаботится о кавычках за вас, поэтому вам не нужно беспокоиться об экранировании строк с кавычками или запятыми. Передайте writerow() ваши необработанные строки, и он сделает все правильно.

Потоковая передача больших файлов CSV

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

В этом примере мы полностью используем генераторы 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)
    return StreamingHttpResponse(
        (writer.writerow(row) for row in rows),
        content_type="text/csv",
        headers={"Content-Disposition": 'attachment; filename="somefilename.csv"'},
    )

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

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

Идея заключается в том, чтобы передать список элементов в ваш шаблон, а шаблон должен вывести запятые в цикле for.

Вот пример, который генерирует тот же CSV-файл, что и выше:

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

Единственное отличие этого примера от предыдущего заключается в том, что здесь используется загрузка шаблона вместо модуля 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 %}

Этот короткий шаблон выполняет итерации по заданным данным и выводит строку CSV для каждой строки. Он использует фильтр шаблона addslashes для обеспечения отсутствия проблем с кавычками.

Другие текстовые форматы

Обратите внимание, что здесь нет ничего особенного для CSV - только конкретный формат вывода. Вы можете использовать любую из этих техник для вывода любого текстового формата, о котором только можно мечтать. Вы также можете использовать аналогичную технику для генерации произвольных двоичных данных; см. пример Как создавать файлы PDF.

Вернуться на верх