Skip to main content

使用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,并向其传递可迭代对象(如列表或元组)。

  • CSV模块会为您处理引号,因此您不必担心使用引号或逗号转义字符串。只需通过 writerow() 你的原始字符串,它会做正确的事情。

在Python上处理Unicode 2

Python 2的 csv 模块不支持Unicode输入。由于Django在内部使用Unicode,这意味着从诸如 HttpRequest 的源读取的字符串可能有问题。有几个选项来处理这个:

有关更多信息,请参阅 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 的例子。