2016-06-23 123 views
1

在视图中,我创建一个使用一个简单的CSV作家CSV完全由Django的HttpResponse对象:如何阅读CSV Django的HTTP响应

response = HttpResponse(content_type='text/csv') 
response['Content-Disposition'] = 'attachment; filename="foobar.csv"' 

writer = csv.writer(response) 

    table_headers = ['Foo', 'Bar'] 
    writer.writerow(table_headers) 

    bunch_of_rows = [['foo', 'bar'], ['foo2', 'bar2']] 
    for row in bunch_of_rows: 
     writer.writerow(row) 

return response 

在一个单元测试,我想测试某些方面这个csv,所以我需要阅读它。我想这样做,像这样:

response = views.myview(args) 

reader = csv.reader(response.content) 

headers = next(reader) 
row_count = 1 + sum(1 for row in reader) 

self.assertEqual(row_count, 3) # header + 1 row for each attempt 
self.assertIn('Foo', headers) 

但测试失败,在headers = next(reader)行:

nose.proxy.Error: iterator should return strings, not int (did you open the file in text mode?) 

我在的HttpResponse源看到response.content咳串回来了作为一个字节字符串,但我不知道正确的方式来处理,让csv.reader正确读取文件。我想我可以只用response替换response.content(因为你写的对象本身,而不是它的内容),但只是导致该错误的微小的变化:

_csv.Error: iterator should return strings, not bytes (did you open the file in text mode?) 

这似乎更接近,但明显仍然错误。阅读csv文档,我认为我无法正确打开文件。如何“打开”这个类似文件的对象,以便csv.reader可以解析它?

回答

3

response.content提供字节。您需要将此解码为字符串:

foo = response.content.decode('utf-8') 

然后使用io.StringIO通过这个字符串到CSV阅读:

import io 
reader = csv.reader(io.StringIO(foo)) 
+0

虽然这两个答案都是有效的,但在我的具体情况下,内容非常小,以至于knbk的答案所提供的性能改进略微超出了该答案的可读性。这两个人都接受了。我最终使用的最终形式是:'reader = csv.reader(StringIO(response.content.decode('utf-8')))'。我选择只导入StringIO,因此省略'io.' – fildred13

2

您可以使用io.TextIOWrapper转换所提供的字节字符串文本流:

import io 
reader = csv.reader(io.TextIOWrapper(io.BytesIO(response.content), encoding='utf-8')) 

这会将字节转换为字符串,因为它们正被读者读取。

+0

替换您的'reader ='行,出现错误:AttributeError:'bytes'对象没有'readable'属性。调查... – fildred13

+0

经过我的初步评论进一步调查,是错误的。要使用这个,你首先需要首先将'response.content'传递给BytesIO。 – miyamoto

+2

你说的对。它的优点是可以解码块,而不是一次解码所有内容,但通常不会有任何区别。在大多数情况下,您的答案更易于使用,但为了完整起见,我将在此留出。 – knbk