Django で CSV を出力する

revision-up-to:17812 (1.4)

このドキュメントでは、 Django のビューを使って動的に CSV (Comma Separated Values: カンマ区切りのフィールドデータ) ファイルを生成する方法について説明 します。 CSV の生成には Python CSV ライブラリ、または Django テンプレートシス テムが使えます。

CSV ライブラリを使う方法

Python には CSV ライブラリモジュール csv がついてきます。Django で csv を使う上での鍵は、モジュールの CSV 生成機能がファイルライクオ ブジェクトを扱えることと、 Django の HttpResponse オ ブジェクトがファイルライクオブジェクトであることです。

一例を示します:

import csv
from django.http import HttpResponse

def some_view(request):
    # 適切な CSV 用ヘッダとともに HttpResponse オブジェクトを生成します。
    response = HttpResponse(mimetype='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

コードとコメントを読めば一目瞭然ですが、いくつか注意しておいたほうがよいで しょう:

  • レスポンスの MIME タイプを text/csv にしておき、ブラウザにド キュメントが CSV であると伝えます。 MIME タイプを指定しないと、出力をHTML と して解釈するので、ブラウザウィンドウに見苦しいごみのような出力が表示されてし まいます。
  • さらに、レスポンスオブジェクトには Content-Disposition ヘッダを指 定しています。このヘッダは CSV ファイルの名前にします。ファイル名には 何を指定しても構いません。この名前は、ブラウザが「名前を付けて保存」 ダイアログなどで使います。
  • CSV 生成 API のフックは簡単です: responsecsv.writer の第 一引数に指定します。 csv.writer クラスはファイルライクオブジェク トを扱うようになっているので、 HttpResponse オ ブジェクトを使えます。
  • CSV ファイルの各行ごとにリストやタプルのようなイテレーション可能オブ ジェクトを渡して writer.writerow を呼び出します。
  • CSV モジュールはクオート処理を行ってくれるので、文字列中にクオートや カンマがあっても心配することはありません。出力したい値を writerow() に渡せば、モジュールが正しく処理してくれます。

Unicode を扱うには

Python の csv モジュールは Unicode の入力をサポートしていません。 Django は内部的に Unicode を使うので HttpRequest のよう なソースから文字列を読み込むことは潜在的に問題を抱えていることになります。この 問題の対処方法はいくつかあります:

  • 全ての Unicode オブジェクトを互換性のあるエンコーディングに手動でエンコード します。
  • python-unicodecsv モジュール を使う。これは Unicode を上品に扱うモジュー ルで、 csv モジュールを容易に置き換えることを目的にしています。

もっと情報が必要なら csv モジュールのドキュメントを参照してください。

テンプレートシステムを使う方法

csv を使う代りに Django テンプレートシステム を使っても CSV を生成できます。この方法よりも csv` モジュールを使った方 がより高水準で実現できますが、完全さのために紹介しておきます。

基本的な考え方は、テンプレートに要素リストを渡し、テンプレート上の for ループの中でコンマとともに出力するというものです。

上と同じ CSV ファイルを生成する例を示します:

from django.http import HttpResponse
from django.core.template import loader, Context

def some_view(request):
    # 適切な CSV 用応答ヘッダを持った HttpResponse オブジェクトを
    # 作成します。
    response = HttpResponse(mimetype='text/csv')
    response['Content-Disposition'] = 'attachment; filename=somefilename.csv'

    # ここではデータをハードコードしていますが、実際にはデータベースな
    # のデータソースから取り出せます。
    csv_data = (
        ('First row', 'Foo', 'Bar', 'Baz'),
        ('Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"),
    )

    t = loader.get_template('my_template_name.html')
    c = Context({
        'data': csv_data,
    })
    response.write(t.render(c))
    return response

前節の例との違いは、 CSV モジュールの代りにテンプレートをロードしている点だ けです。それ以外のコード、例えば mimetype='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 %}

このテンプレートはきわめて基本的なものです。テンプレートは指定されたデータ に渡って繰り返し CSV の各行を表示してゆきます。クオートで問題が生じないよう、 addslashes テンプレートフィルタを使っています。データに引用符 (一重と二重の両方) が入っていないと保証できる場合は addslashes フィルタを外してもかまいません。

他のテキストベース形式のデータを出力するには

ここで示した例の大部分は、 CSV 固有の内容ではないことに注意してください。 CSV に関する部分といえば、出力形式くらいのものです。同じようなテクニックを 使えば、 どんなテキスト形式のデータでも生成できるのです。バイナリデータを生 成するときにも、同じようなテクニックを使えます。 Django で PDF を出力する を参照してください。