如何创建 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",
        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 文件的名称。此文件名是任意的;您可以随意命名。它将在浏览器的“另存为...”对话框等中使用。

  • 您可以通过将 response 作为第一个参数传递给 csv.writer 来连接到 CSV 生成 API。 csv.writer 函数需要一个类文件对象,而 HttpResponse 对象符合要求。

  • 对于 CSV 文件中的每一行,调用 writer.writerow,并将 可迭代对象 传递给它。

  • CSV 模块会为您处理引用,因此您不必担心转义包含引号或逗号的字符串。将您的原始字符串传递给 writerow(),它将执行正确的操作。

流式传输大型 CSV 文件

当处理生成非常大的响应的视图时,您可能需要考虑使用 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"'},
    )

使用模板系统

或者,您可以使用 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 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 文件 以了解示例。

返回顶部