手动添加行到StreamingHttpResponse(Django)

时间:2017-08-08 21:19:26

标签: python django csv

我正在使用Django的StreamingHttpResponse来动态传​​输大型CSV文件。根据{{​​3}},迭代器被传递给响应的streaming_content参数:

import csv
from django.http import StreamingHttpResponse

def get_headers():
    return ['field1', 'field2', 'field3']

def get_data(item):
    return {
        'field1': item.field1,
        'field2': item.field2,
        'field3': item.field3,
    }

# StreamingHttpResponse requires a File-like class that has a 'write' method
class Echo(object):
    def write(self, value):
        return value


def get_response(queryset):
    writer = csv.DictWriter(Echo(), fieldnames=get_headers())
    writer.writeheader() # this line does not work

    response = StreamingHttpResponse(
        # the iterator
        streaming_content=(writer.writerow(get_data(item)) for item in queryset),
        content_type='text/csv',
    )
    response['Content-Disposition'] = 'attachment;filename=items.csv'

    return response

我的问题是:如何在CSV编写器上手动编写行?手动调用writer.writerow(data)或writer.writeheader()(也在内部调用writerow())似乎不会写入数据集,而只是将来自streaming_content的生成/流数据写入输出数据集。 / p>

2 个答案:

答案 0 :(得分:4)

答案是使用生成器函数产生结果,而不是动态计算它们(在StreamingHttpResponse的streaming_content参数内)并使用我们创建的伪缓冲区(Echo Class)来向响应写入一行:

import csv
from django.http import StreamingHttpResponse

def get_headers():
    return ['field1', 'field2', 'field3']

def get_data(item):
    return {
        'field1': item.field1,
        'field2': item.field2,
        'field3': item.field3,
    }

# StreamingHttpResponse requires a File-like class that has a 'write' method
class Echo(object):
    def write(self, value):
        return value

def iter_items(items, pseudo_buffer):
    writer = csv.DictWriter(pseudo_buffer, fieldnames=get_headers())
    yield pseudo_buffer.write(get_headers())

    for item in items:
        yield writer.writerow(get_data(item))

def get_response(queryset):
    response = StreamingHttpResponse(
        streaming_content=(iter_items(queryset, Echo())),
        content_type='text/csv',
    )
    response['Content-Disposition'] = 'attachment;filename=items.csv'
    return response

答案 1 :(得分:0)

建议的解决方案实际上可能导致CSV错误/不匹配(报头与数据不匹配)。您想要用以下内容替换受影响的部分:

header = dict(zip(fieldnames, fieldnames))
yield writer.writerow(header)

相反。这来自writeheader https://github.com/python/cpython/blob/08045391a7aa87d4fbd3e8ef4c852c2fa4e81a8a/Lib/csv.py#L141:L143

的实现

由于某些原因,yield的运行状况不佳

希望这对以后的人有所帮助:)

还请注意,由于此PR,如果使用python 3.8+,则无需修复:https://bugs.python.org/issue27497