How to create CSV output

このドキュメントでは、Django のビューを使って動的に CSV (Comma Separated Values) を出力する方法を説明しています。そのためには、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',
        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

コードとコメントが自分自身の意味を説明しているはずですが、いくつかの点には言及しておくべきでしょう。

  • レスポンスは特別な MIME タイプ (text/csv) を受け取ります。これにより、ブラウザは、ドキュメントが HTML ではなく CSV ファイルであることを判断します。もしこの MIME タイプの設定を忘れると、ブラウザは出力を HTML と解釈し、ブラウザウィンドウに醜くて恐ろしいぐちゃぐちゃの文字列が現れてしまう恐れがあります。
  • レスポンスには、追加の Content-Disposition ヘッダに CSV のファイル名を設定することができます。ファイル名はどんなものでも構いません。ここで指定した名前は、ブラウザが「別名で保存」ダイアログなどで使用します。
  • You can hook into the CSV-generation API by passing response as the first argument to csv.writer. The csv.writer function expects a file-like object, and HttpResponse objects fit the bill.
  • For each row in your CSV file, call writer.writerow, passing it an iterable.
  • The CSV module takes care of quoting for you, so you don't have to worry about escaping strings with quotes or commas in them. Pass writerow() your raw strings, and it'll do the right thing.

サイズの大きな CSV ファイルをストリーミングする

大きなサイズのレスポンスを生成するビューを取り扱うときには、代わりに 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 で例を参照してください。