# Django 6.0.7 ドキュメント

# CSVを出力する

このドキュメントでは、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 のファイル名を設定することができます。ファイル名はどんなものでも構いません。ここで指定した名前は、ブラウザが「別名で保存」ダイアログなどで使用します。

- CSV 生成 API にフックするには、`csv.writer` の最初の引数として `response` を渡すだけです。`csv.writer` 関数はファイルライクなオブジェクトを要求しますが、`HttpResponse` オブジェクトはこの要求に適合しています。

- CSV ファイルの行ごとに `writer.writerow` を呼び、iterable を渡します。

- CSV モジュールは引用を適切に処理するので、引用符やカンマでの文字列のエスケープについて心配する必要はありません。生の文字列を `writerow()` に渡すことで適切に処理されます。

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

大きなサイズのレスポンスを生成するビューを取り扱うときには、代わりに Django の `StreamingHttpResponse` の使用を考えてみてください。例えば、生成に長時間を要するファイルをストリーミングことで、サーバーがレスポンスを生成している間にタイムアウトしてしまうような接続でも、ロードバランサが接続をドロップすることを防げます。

In this example, we make full use of Python generators to efficiently handle the assembly and transmission of a large CSV file. Rows are batched together to reduce HTTP overhead and improve compression efficiency when used with `GZipMiddleware`:

    import csv
    from itertools import batched

    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)

        def stream_batched_rows():
            for batch in batched(rows, 100):
                yield "".join(writer.writerow(row) for row in batch)

        return StreamingHttpResponse(
            stream_batched_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 hardcoded 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 %}

この短いテンプレートは与えられたデータを繰り返し処理し、各行の CSV 行を表示します。引用符の問題がないように `addslashes` テンプレートフィルタを使用します。

## 他のテキストベースのフォーマット

ここで説明した CSV (特定の出力フォーマットの 1 つ)のような、フォーマット特有の説明は特にありません。あなたの望むテキストベースのフォーマットを出力するときには、ここで説明したテクニックを活用してみてください。また、任意のバイナリデータを生成するときにも、ここでのテクニックが参考になります; PDFファイルを作成する で例を参照してください。