使用 Django 输出 CSV — Django 文档

来自菜鸟教程
Django/docs/2.2.x/howto/outputting-csv
跳转至:导航、​搜索

利用 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.writercsv.writer 函数需要一个类似文件的对象,而 HttpResponse 对象符合要求。
  • 对于 CSV 文件中的每一行,调用 writer.writerow,传递一个 iterable
  • 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)
    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 模块低级,但为了完整起见,这里提供了解决方案。

这里的想法是将项目列表传递给您的模板,并让模板在 :ttag:`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。 它使用 :tfilter:`addslashes` 模板过滤器来确保引号没有任何问题。


其它文本格式

请注意,这里没有特别针对 CSV 的内容——只是特定的输出格式。 您可以使用这些技术中的任何一种来输出您梦寐以求的任何基于文本的格式。 您也可以使用类似的技术来生成任意二进制数据; 有关示例,请参阅 使用 Django 输出 PDF。