Вывод CSV с помощью Django¶
Этот документ объясняет, как динамически выводить 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')
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
Код и комментарии должны быть понятны, но несколько моментов заслуживают упоминания:
- Ответ получает специальный 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)
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. Это более низкий уровень, чем использование удобного модуля 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')
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
Единственное отличие этого примера от предыдущего заключается в том, что здесь используется загрузка шаблона вместо модуля 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-файлов с помощью Django.