本文介绍如何用 Django 的视图动态输出 CSV (Comma Separated Values)。要达到目的,你可以使用 Python 的 CSV 库或 Django 的模板系统。
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
代码和注释应该是不言自明的,但是有几件事值得提一下:
Content-Disposition
头,其中包含了 CSV 文件的名称。文件名是任意的;随便你怎么叫。它会被浏览器在 "保存为……" 对话框中用到。response
作为第一个参数传给 csv.writer
来接入生成 CSV API 。 csv.writer
函数期望一个类文件对象,而 HttpResponse
对象满足该要求。writer.writerrow
,传入一个 iterable 参数。writerow()
传入原始字符串,它为你处理好一切。当处理生成很多回复的视图时,你可能要考虑使用 Django 的 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)
return StreamingHttpResponse(
(writer.writerow(row) for row in rows),
content_type="text/csv",
headers={"Content-Disposition": 'attachment; filename="somefilename.csv"'},
)
或者,你也能用 Djano 模板系统 生成 CSV。这比使用方便的 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 文件 的例子。
5月 31, 2023