使用Django输出CSV¶
本文解释如何使用Django视图动态输出CSV(逗号分隔值)。为此,您可以使用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,这将导致在浏览器窗口丑陋,可怕的gobbledygook。
该响应获取一个附加的
Content-Disposition
头,其中包含CSV文件的名称。这个文件名是任意的;无论你想要什么。它将由浏览器在“另存为...”对话框等中使用。挂钩到CSV生成API很容易:只需将
response
作为第一个参数传递给csv.writer
。csv.writer
函数需要一个类似文件的对象,HttpResponse
对象适合账单。对于CSV文件中的每一行,调用
writer.writerow
,并向其传递可迭代对象(如列表或元组)。CSV模块会为您处理引号,因此您不必担心使用引号或逗号转义字符串。只需通过
writerow()
你的原始字符串,它会做正确的事情。
在Python上处理Unicode 2
Python 2的 csv
模块不支持Unicode输入。由于Django在内部使用Unicode,这意味着从诸如 HttpRequest
的源读取的字符串可能有问题。有几个选项来处理这个:
将所有Unicode对象手动编码为兼容的编码。
使用 csv module’s examples section 中提供的
UnicodeWriter
类。使用 python-unicodecsv module,其旨在成为替代
csv
的优雅处理Unicode的替代。
有关更多信息,请参阅 csv
模块的Python文档。
流式传输大型CSV文件¶
在处理产生非常大响应的视图时,您可能需要考虑使用Django的 StreamingHttpResponse
。例如,通过流式传输需要很长时间才能生成的文件,可以避免负载平衡器丢弃可能在服务器生成响应时超时的连接。
在这个例子中,我们充分利用Python生成器来有效地处理大型CSV文件的汇编和传输:
import csv
from django.utils.six.moves import range
from django.http import StreamingHttpResponse
class Echo(object):
"""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模板系统 生成CSV。这是比使用方便的Python csv
模块更低级的,但是这里提出的解决方案是为了完整性。
这里的想法是将一个项目列表传递给您的模板,并让模板在 for
循环中输出逗号。
下面是一个示例,它生成与上面相同的CSV文件:
from django.http import HttpResponse
from django.template import loader, Context
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 = Context({
'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 - 只是具体的输出格式。您可以使用这些技术中的任何一种输出任何基于文本的格式,你可以梦想。您也可以使用类似的技术来生成任意二进制数据;见 使用Django输出PDF 的例子。