本文说明如何通过 Django 视图动态输出 CSV(逗号分隔值)。你可以使用 Python 的 CSV 库,也可以使用 Django 模板系统。
使用 Python CSV 库
Python 自带 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,页面就会显示混乱的内容。
– 响应还包含 Content-Disposition 头,其中给出 CSV 文件名。文件名可以自行选择,浏览器会在“另存为”等对话框中使用它。
– 把 response 作为第一个参数传给 csv.writer,即可使用 CSV 生成 API。该函数要求类文件对象,HttpResponse 满足这个要求。
– 对每一行调用 writer.writerow,并传入一个可迭代对象。
– CSV 模块会处理引号和逗号的转义。把原始字符串交给 writerow() 即可。
流式传输大型 CSV 文件
如果视图生成的响应很大,可以考虑改用 StreamingHttpResponse。对于生成时间很长的文件,流式发送可以避免负载均衡器在服务器生成响应期间因超时而断开连接。
下面的示例充分利用 Python 生成器,高效组装并发送大型 CSV。它把多行合成一批,减少 HTTP 开销,并在使用 GZipMiddleware 时改善压缩效率:
import csv
from itertools import batched
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)
def stream_batched_rows():
for batch in batched(rows, 100):
yield "".join(writer.writerow(row) for row in batch)
return StreamingHttpResponse(
stream_batched_rows(),
content_type="text/csv",
headers={"Content-Disposition": 'attachment; filename="somefilename.csv"'},
)
使用模板系统
另一种方式是用 Django 模板系统生成 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 hardcoded 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,只有输出格式是 CSV。你可以用这两种技术输出其他文本格式。类似技术也能生成任意二进制数据,例如官方的 生成 PDF 文件指南。
技术核对与勘误
以下说明针对原文示例,代码保持原样。流式示例使用 itertools.batched,需要 Python 3.12 或更新版本。模板固定访问 row.0 至 row.4,但示例两行分别有 4 个和 6 个字段,所以模板会多输出一个空字段、遗漏第二行的第六个字段,不能生成与 csv.writer 相同的 CSV。addslashes 的反斜杠转义也不同于 Python CSV 默认的双引号加倍规则;生产导出应优先使用 csv.writer。原文的 65,536 行注释反映历史背景,例如 Excel 97–2003 的单表行数上限;它只是这里的示例范围,并非现代电子表格的通用上限。
来源与许可
原文:How to create CSV output。作者:Django Software Foundation 与贡献者。本文为中文翻译,代码、示例数据与输出逻辑保留原文。
Django 文档随项目采用 BSD 3-Clause 许可证;以下保留完整版权声明、条件与免责声明,亦可查阅 官方许可证。
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of Django nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
本文适用于 Django 6.1。












暂无评论内容