mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			151 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| ==========================
 | |
| Outputting CSV with Django
 | |
| ==========================
 | |
| 
 | |
| This document explains how to output CSV (Comma Separated Values) dynamically
 | |
| using Django views. To do this, you can either use the Python CSV library or the
 | |
| Django template system.
 | |
| 
 | |
| Using the Python CSV library
 | |
| ============================
 | |
| 
 | |
| Python comes with a CSV library, :mod:`csv`. The key to using it with Django is
 | |
| that the :mod:`csv` module's CSV-creation capability acts on file-like objects,
 | |
| and Django's :class:`~django.http.HttpResponse` objects are file-like objects.
 | |
| 
 | |
| Here's an example::
 | |
| 
 | |
|     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')
 | |
|         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
 | |
| 
 | |
| The code and comments should be self-explanatory, but a few things deserve a
 | |
| mention:
 | |
| 
 | |
| * The response gets a special MIME type, :mimetype:`text/csv`. This tells
 | |
|   browsers that the document is a CSV file, rather than an HTML file. If
 | |
|   you 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, which
 | |
|   contains the name of the CSV file. This filename is arbitrary; call it
 | |
|   whatever you want. It'll be used by browsers in the "Save as..." dialog, etc.
 | |
| 
 | |
| * Hooking into the CSV-generation API is easy: Just pass ``response`` as the
 | |
|   first argument to ``csv.writer``. The ``csv.writer`` function expects a
 | |
|   file-like object, and :class:`~django.http.HttpResponse` objects fit the
 | |
|   bill.
 | |
| 
 | |
| * For each row in your CSV file, call ``writer.writerow``, passing it an
 | |
|   iterable object such as a list or tuple.
 | |
| 
 | |
| * 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. Just pass
 | |
|   ``writerow()`` your raw strings, and it'll do the right thing.
 | |
| 
 | |
| .. _streaming-csv-files:
 | |
| 
 | |
| Streaming large CSV files
 | |
| -------------------------
 | |
| 
 | |
| When dealing with views that generate very large responses, you might want to
 | |
| consider using Django's :class:`~django.http.StreamingHttpResponse` instead.
 | |
| For example, by streaming a file that takes a long time to generate you can
 | |
| avoid a load balancer dropping a connection that might have otherwise timed out
 | |
| while the server was generating the response.
 | |
| 
 | |
| In this example, we make full use of Python generators to efficiently handle
 | |
| the assembly and transmission of a large CSV file::
 | |
| 
 | |
|     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)
 | |
|         response = StreamingHttpResponse((writer.writerow(row) for row in rows),
 | |
|                                          content_type="text/csv")
 | |
|         response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
 | |
|         return response
 | |
| 
 | |
| Using the template system
 | |
| =========================
 | |
| 
 | |
| Alternatively, you can use the :doc:`Django template system </topics/templates>`
 | |
| to generate CSV. This is lower-level than using the convenient Python :mod:`csv`
 | |
| module, but the solution is presented here for completeness.
 | |
| 
 | |
| The idea here is to pass a list of items to your template, and have the
 | |
| template output the commas in a :ttag:`for` loop.
 | |
| 
 | |
| Here's an example, which generates the same CSV file as above::
 | |
| 
 | |
|     from django.http import HttpResponse
 | |
|     from django.template import loader, Context
 | |
| 
 | |
|     def some_view(request):
 | |
|         # Create the HttpResponse object with the appropriate CSV header.
 | |
|         response = HttpResponse(content_type='text/csv')
 | |
|         response['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 = Context({
 | |
|             'data': csv_data,
 | |
|         })
 | |
|         response.write(t.render(c))
 | |
|         return response
 | |
| 
 | |
| The only difference between this example and the previous example is that this
 | |
| one 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:
 | |
| 
 | |
| .. code-block:: html+django
 | |
| 
 | |
|     {% for row in data %}"{{ row.0|addslashes }}", "{{ row.1|addslashes }}", "{{ row.2|addslashes }}", "{{ row.3|addslashes }}", "{{ row.4|addslashes }}"
 | |
|     {% endfor %}
 | |
| 
 | |
| This template is quite basic. It just iterates over the given data and displays
 | |
| a line of CSV for each row. It uses the :tfilter:`addslashes` template filter to
 | |
| ensure there aren't any problems with quotes.
 | |
| 
 | |
| Other text-based formats
 | |
| ========================
 | |
| 
 | |
| Notice that there isn't very much specific to CSV here -- just the specific
 | |
| output format. You can use either of these techniques to output any text-based
 | |
| format you can dream of. You can also use a similar technique to generate
 | |
| arbitrary binary data; see :doc:`/howto/outputting-pdf` for an example.
 |