1. ========================
    
  2. How to create CSV output
    
  3. ========================
    
  4. 
    
  5. This document explains how to output CSV (Comma Separated Values) dynamically
    
  6. using Django views. To do this, you can either use the Python CSV library or the
    
  7. Django template system.
    
  8. 
    
  9. Using the Python CSV library
    
  10. ============================
    
  11. 
    
  12. Python comes with a CSV library, :mod:`csv`. The key to using it with Django is
    
  13. that the :mod:`csv` module's CSV-creation capability acts on file-like objects,
    
  14. and Django's :class:`~django.http.HttpResponse` objects are file-like objects.
    
  15. 
    
  16. Here's an example::
    
  17. 
    
  18.     import csv
    
  19.     from django.http import HttpResponse
    
  20. 
    
  21.     def some_view(request):
    
  22.         # Create the HttpResponse object with the appropriate CSV header.
    
  23.         response = HttpResponse(
    
  24.             content_type='text/csv',
    
  25.             headers={'Content-Disposition': 'attachment; filename="somefilename.csv"'},
    
  26.         )
    
  27. 
    
  28.         writer = csv.writer(response)
    
  29.         writer.writerow(['First row', 'Foo', 'Bar', 'Baz'])
    
  30.         writer.writerow(['Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"])
    
  31. 
    
  32.         return response
    
  33. 
    
  34. The code and comments should be self-explanatory, but a few things deserve a
    
  35. mention:
    
  36. 
    
  37. * The response gets a special MIME type, :mimetype:`text/csv`. This tells
    
  38.   browsers that the document is a CSV file, rather than an HTML file. If
    
  39.   you leave this off, browsers will probably interpret the output as HTML,
    
  40.   which will result in ugly, scary gobbledygook in the browser window.
    
  41. 
    
  42. * The response gets an additional ``Content-Disposition`` header, which
    
  43.   contains the name of the CSV file. This filename is arbitrary; call it
    
  44.   whatever you want. It'll be used by browsers in the "Save as..." dialog, etc.
    
  45. 
    
  46. * You can hook into the CSV-generation API by passing ``response`` as the first
    
  47.   argument to ``csv.writer``. The ``csv.writer`` function expects a file-like
    
  48.   object, and :class:`~django.http.HttpResponse` objects fit the bill.
    
  49. 
    
  50. * For each row in your CSV file, call ``writer.writerow``, passing it an
    
  51.   :term:`iterable`.
    
  52. 
    
  53. * The CSV module takes care of quoting for you, so you don't have to worry
    
  54.   about escaping strings with quotes or commas in them. Pass ``writerow()``
    
  55.   your raw strings, and it'll do the right thing.
    
  56. 
    
  57. .. _streaming-csv-files:
    
  58. 
    
  59. Streaming large CSV files
    
  60. -------------------------
    
  61. 
    
  62. When dealing with views that generate very large responses, you might want to
    
  63. consider using Django's :class:`~django.http.StreamingHttpResponse` instead.
    
  64. For example, by streaming a file that takes a long time to generate you can
    
  65. avoid a load balancer dropping a connection that might have otherwise timed out
    
  66. while the server was generating the response.
    
  67. 
    
  68. In this example, we make full use of Python generators to efficiently handle
    
  69. the assembly and transmission of a large CSV file::
    
  70. 
    
  71.     import csv
    
  72. 
    
  73.     from django.http import StreamingHttpResponse
    
  74. 
    
  75.     class Echo:
    
  76.         """An object that implements just the write method of the file-like
    
  77.         interface.
    
  78.         """
    
  79.         def write(self, value):
    
  80.             """Write the value by returning it, instead of storing in a buffer."""
    
  81.             return value
    
  82. 
    
  83.     def some_streaming_csv_view(request):
    
  84.         """A view that streams a large CSV file."""
    
  85.         # Generate a sequence of rows. The range is based on the maximum number of
    
  86.         # rows that can be handled by a single sheet in most spreadsheet
    
  87.         # applications.
    
  88.         rows = (["Row {}".format(idx), str(idx)] for idx in range(65536))
    
  89.         pseudo_buffer = Echo()
    
  90.         writer = csv.writer(pseudo_buffer)
    
  91.         return StreamingHttpResponse(
    
  92.             (writer.writerow(row) for row in rows),
    
  93.             content_type="text/csv",
    
  94.             headers={'Content-Disposition': 'attachment; filename="somefilename.csv"'},
    
  95.         )
    
  96. 
    
  97. Using the template system
    
  98. =========================
    
  99. 
    
  100. Alternatively, you can use the :doc:`Django template system </topics/templates>`
    
  101. to generate CSV. This is lower-level than using the convenient Python :mod:`csv`
    
  102. module, but the solution is presented here for completeness.
    
  103. 
    
  104. The idea here is to pass a list of items to your template, and have the
    
  105. template output the commas in a :ttag:`for` loop.
    
  106. 
    
  107. Here's an example, which generates the same CSV file as above::
    
  108. 
    
  109.     from django.http import HttpResponse
    
  110.     from django.template import loader
    
  111. 
    
  112.     def some_view(request):
    
  113.         # Create the HttpResponse object with the appropriate CSV header.
    
  114.         response = HttpResponse(
    
  115.             content_type='text/csv',
    
  116.             headers={'Content-Disposition': 'attachment; filename="somefilename.csv"'},
    
  117.         )
    
  118. 
    
  119.         # The data is hard-coded here, but you could load it from a database or
    
  120.         # some other source.
    
  121.         csv_data = (
    
  122.             ('First row', 'Foo', 'Bar', 'Baz'),
    
  123.             ('Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"),
    
  124.         )
    
  125. 
    
  126.         t = loader.get_template('my_template_name.txt')
    
  127.         c = {'data': csv_data}
    
  128.         response.write(t.render(c))
    
  129.         return response
    
  130. 
    
  131. The only difference between this example and the previous example is that this
    
  132. one uses template loading instead of the CSV module. The rest of the code --
    
  133. such as the ``content_type='text/csv'`` -- is the same.
    
  134. 
    
  135. Then, create the template ``my_template_name.txt``, with this template code:
    
  136. 
    
  137. .. code-block:: html+django
    
  138. 
    
  139.     {% for row in data %}"{{ row.0|addslashes }}", "{{ row.1|addslashes }}", "{{ row.2|addslashes }}", "{{ row.3|addslashes }}", "{{ row.4|addslashes }}"
    
  140.     {% endfor %}
    
  141. 
    
  142. This short template iterates over the given data and displays a line of CSV for
    
  143. each row. It uses the :tfilter:`addslashes` template filter to ensure there
    
  144. aren't any problems with quotes.
    
  145. 
    
  146. Other text-based formats
    
  147. ========================
    
  148. 
    
  149. Notice that there isn't very much specific to CSV here -- just the specific
    
  150. output format. You can use either of these techniques to output any text-based
    
  151. format you can dream of. You can also use a similar technique to generate
    
  152. arbitrary binary data; see :doc:`/howto/outputting-pdf` for an example.