這篇文檔闡述了如何通過使用Django視圖動(dòng)態(tài)輸出CSV (Comma Separated Values)。 你可以使用Python CSV 庫或者Django的模板系統(tǒng)來達(dá)到目的。
Python自帶了CSV庫,csv。在Django中使用它的關(guān)鍵是,csv模塊的CSV創(chuàng)建功能作用于類似于文件的對(duì)象,并且Django的HttpResponse對(duì)象就是類似于文件的對(duì)象。
這里是個(gè)例子:
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
代碼和注釋是不用多說的,但是一些事情需要提醒一下:
text/csv。這會(huì)告訴瀏覽器,文檔是個(gè)CSV文件而不是HTML文件。如果你把它去掉,瀏覽器可能會(huì)把輸出解釋為HTML,會(huì)在瀏覽器窗口中顯示一篇丑陋的、可怕的官樣文章。Content-Disposition協(xié)議頭,它含有CSV文件的名稱。文件名可以是任意的;你想把它叫做什么都可以。瀏覽器會(huì)在”另存為“對(duì)話框中使用它,或者其它。response作為第一個(gè)參數(shù)傳遞給csv.writer。csv.writer 函數(shù)接受一個(gè)類似于文件的對(duì)象,而HttpResponse 對(duì)象正好合適。writer.writerow,向它傳遞一個(gè)可迭代的對(duì)象比如列表或者元組。writerow()傳遞你的原始字符串,它就會(huì)執(zhí)行正確的操作。在Python 2中處理Unicode
Python2的
csv模塊不支持Unicode輸入。由于Django在內(nèi)部使用Unicode,這意味著從一些來源比如HttpRequest讀出來的字符串可能導(dǎo)致潛在的問題。有一些選項(xiàng)用于處理它:
- 手動(dòng)將所有Unicode對(duì)象編碼為兼容的編碼。
- 使用csv模塊示例章節(jié)中提供的
UnicodeWriter類。- 使用python-unicodecsv 模塊,它作為
csv模塊隨時(shí)可用的替代方案,能夠優(yōu)雅地處理Unicode。更多信息請(qǐng)見
csv模塊的Python文檔。
當(dāng)處理生成大尺寸響應(yīng)的視圖時(shí),你可能想要使用Django的StreamingHttpResponse類。例如,通過流式傳輸需要長(zhǎng)時(shí)間來生成的文件,可以避免負(fù)載均衡器在服務(wù)器生成響應(yīng)的時(shí)候斷掉連接。
在這個(gè)例子中,我們利用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模板系統(tǒng)來生成CSV。比起便捷的Python csv模板來說,這樣比較低級(jí),但是為了完整性,這個(gè)解決方案還是在這里展示一下。
它的想法是,傳遞一個(gè)項(xiàng)目的列表給你的模板,并且讓模板在for循環(huán)中輸出逗號(hào)。
這里是一個(gè)例子,它像上面一樣生成相同的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
這個(gè)例子和上一個(gè)例子之間唯一的不同就是,這個(gè)例子使用模板來加載,而不是CSV模塊。代碼的結(jié)果 -- 比如content_type='text/csv' -- 都是相同的。
然后,創(chuàng)建模板my_template_name.txt,帶有以下模板代碼:
{% for row in data %}"{{ row.0|addslashes }}", "{{ row.1|addslashes }}", "{{ row.2|addslashes }}", "{{ row.3|addslashes }}", "{{ row.4|addslashes }}"
{% endfor %}
這個(gè)模板十分基礎(chǔ)。它僅僅遍歷了提供的數(shù)據(jù),并且對(duì)于每一行都展示了一行CSV。它使用了addslashes模板過濾器來確保沒有任何引用上的問題。
要注意對(duì)于 CSV來說,這里并沒有什么特別之處 -- 只是特定了輸出格式。你可以使用這些技巧中的任何一個(gè),來輸出任何你想要的,基于文本的格式。你也可以使用相似的技巧來生成任意的二進(jìn)制數(shù)據(jù)。例子請(qǐng)參見在Django中輸出PDF。
譯者:Django 文檔協(xié)作翻譯小組,原文:Generating CSV。
本文以 CC BY-NC-SA 3.0 協(xié)議發(fā)布,轉(zhuǎn)載請(qǐng)保留作者署名和文章出處。
Django 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606。