Outputting CSV with Django

This document explains how to output CSV (Comma Separated Values) dynamicallyusing Django views. To do this, you can either use the Python CSV library or theDjango template system.

Using the Python CSV library

Python comes with a CSV library, csv. The key to using it with Django isthat the csv module’s CSV-creation capability acts on file-like objects,and Django’s HttpResponse objects are file-like objects.

Here’s an example:

  1. import csv
  2. from django.http import HttpResponse
  3.  
  4. def some_view(request):
  5. # Create the HttpResponse object with the appropriate CSV header.
  6. response = HttpResponse(content_type='text/csv')
  7. response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
  8.  
  9. writer = csv.writer(response)
  10. writer.writerow(['First row', 'Foo', 'Bar', 'Baz'])
  11. writer.writerow(['Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"])
  12.  
  13. return response

The code and comments should be self-explanatory, but a few things deserve amention:

  • The response gets a special MIME type, text/csv. This tellsbrowsers that the document is a CSV file, rather than an HTML file. Ifyou leave this off, browsers will probably interpret the output as HTML,which will result in ugly, scary gobbledygook in the browser window.
  • The response gets an additional Content-Disposition header, whichcontains the name of the CSV file. This filename is arbitrary; call itwhatever you want. It’ll be used by browsers in the “Save as…” dialog, etc.
  • You can hook into the CSV-generation API by passing response as the firstargument to csv.writer. The csv.writer function expects a file-likeobject, and HttpResponse objects fit the bill.
  • For each row in your CSV file, call writer.writerow, passing it aniterable.
  • The CSV module takes care of quoting for you, so you don’t have to worryabout escaping strings with quotes or commas in them. Pass writerow()your raw strings, and it’ll do the right thing.

Streaming large CSV files

When dealing with views that generate very large responses, you might want toconsider using Django’s StreamingHttpResponse instead.For example, by streaming a file that takes a long time to generate you canavoid a load balancer dropping a connection that might have otherwise timed outwhile the server was generating the response.

In this example, we make full use of Python generators to efficiently handlethe assembly and transmission of a large CSV file:

  1. import csv
  2.  
  3. from django.http import StreamingHttpResponse
  4.  
  5. class Echo:
  6. """An object that implements just the write method of the file-like
  7. interface.
  8. """
  9. def write(self, value):
  10. """Write the value by returning it, instead of storing in a buffer."""
  11. return value
  12.  
  13. def some_streaming_csv_view(request):
  14. """A view that streams a large CSV file."""
  15. # Generate a sequence of rows. The range is based on the maximum number of
  16. # rows that can be handled by a single sheet in most spreadsheet
  17. # applications.
  18. rows = (["Row {}".format(idx), str(idx)] for idx in range(65536))
  19. pseudo_buffer = Echo()
  20. writer = csv.writer(pseudo_buffer)
  21. response = StreamingHttpResponse((writer.writerow(row) for row in rows),
  22. content_type="text/csv")
  23. response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
  24. return response

Using the template system

Alternatively, you can use the Django template systemto generate CSV. This is lower-level than using the convenient Python csvmodule, but the solution is presented here for completeness.

The idea here is to pass a list of items to your template, and have thetemplate output the commas in a for loop.

Here’s an example, which generates the same CSV file as above:

  1. from django.http import HttpResponse
  2. from django.template import Context, loader
  3.  
  4. def some_view(request):
  5. # Create the HttpResponse object with the appropriate CSV header.
  6. response = HttpResponse(content_type='text/csv')
  7. response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
  8.  
  9. # The data is hard-coded here, but you could load it from a database or
  10. # some other source.
  11. csv_data = (
  12. ('First row', 'Foo', 'Bar', 'Baz'),
  13. ('Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"),
  14. )
  15.  
  16. t = loader.get_template('my_template_name.txt')
  17. c = Context({
  18. 'data': csv_data,
  19. })
  20. response.write(t.render(c))
  21. return response

The only difference between this example and the previous example is that thisone uses template loading instead of the CSV module. The rest of the code –such as the content_type='text/csv' – is the same.

Then, create the template my_template_name.txt, with this template code:

  1. {% for row in data %}"{{ row.0|addslashes }}", "{{ row.1|addslashes }}", "{{ row.2|addslashes }}", "{{ row.3|addslashes }}", "{{ row.4|addslashes }}"
  2. {% endfor %}

This short template iterates over the given data and displays a line of CSV foreach row. It uses the addslashes template filter to ensure therearen’t any problems with quotes.

Other text-based formats

Notice that there isn’t very much specific to CSV here – just the specificoutput format. You can use either of these techniques to output any text-basedformat you can dream of. You can also use a similar technique to generatearbitrary binary data; see Outputting PDFs with Django for an example.