このドキュメントでは、Django のビューを使って動的に CSV (Comma Separated Values) を出力する方法を説明しています。そのためには、Python のCSV ライブラリか Django のテンプレートシステムを使います。
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',
headers={'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
コードとコメントが自分自身の意味を説明しているはずですが、いくつかの点には言及しておくべきでしょう。
Content-Disposition
ヘッダに CSV のファイル名を設定することができます。ファイル名はどんなものでも構いません。ここで指定した名前は、ブラウザが「別名で保存」ダイアログなどで使用します。response
as the first
argument to csv.writer
. The csv.writer
function expects a file-like
object, and HttpResponse
objects fit the bill.writer.writerow
, passing it an
iterable.writerow()
your raw strings, and it'll do the right thing.大きなサイズのレスポンスを生成するビューを取り扱うときには、代わりに 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)
return StreamingHttpResponse(
(writer.writerow(row) for row in rows),
content_type="text/csv",
headers={'Content-Disposition': 'attachment; filename="somefilename.csv"'},
)
もしくは、Django のテンプレートシステム を使って CSV を生成することもできます。Python の csv
モジュールの利便性には劣りますが、完全性のために提供されています。
考え方としては、テンプレートに項目のリストを渡すというものです。そして、テンプレートの 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',
headers={'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 %}
This short template iterates over the given data and displays a line of CSV for
each row. It uses the addslashes
template filter to ensure there
aren't any problems with quotes.
ここで説明した CSV (特定の出力フォーマットの 1 つ)のような、フォーマット特有の説明は特にありません。あなたの望むテキストベースのフォーマットを出力するときには、ここで説明したテクニックを活用してみてください。また、任意のバイナリデータを生成するときにも、ここでのテクニックが参考になります; How to create PDF files で例を参照してください。
2022年6月01日