Usually when we talk about developing Web sites, we’re talking about producing HTML. Of course, there’s a lot more to the Web than HTML; we use the Web to distribute data in all sorts of formats: RSS,
PDFs, images, and so forth.
So far, we’ve focused on the common case of HTML production, but in this chapter we’ll take a detour and look at using Django to produce other types of content. Django has convenient built-in tools that you can use to produce some common non-HTML content:
- Comma-delimited (CSV) files for importing into spreadsheet applications.
- PDF files.
- RSS/Atom syndication feeds.
- Sitemaps (an XML format originally developed by Google that gives hints to search engines).
We’ll examine each of those tools a little later, but first we’ll cover the basic principles.
The basics: views and MIME types
Recall from Chapter 2 that a view function is simply a Python function that takes a Web request and returns a Web response. This response can be the HTML contents of a Web page, or a redirect, or a 404 error, or an XML document, or an image â€¦or anything, really. More formally, a Django view function must:
- Accept an
HttpRequestinstance as its first argument; and
- Return an
The key to returning non-HTML content from a view lies in the
HttpResponse class, specifically the
content_type argument. By default, Django sets
content_type to “text/html”. You can however, set
content_type to any of the official Internet media types (MIME types) managed by IANA.
By tweaking the MIME type, we can indicate to the browser that we’ve returned a response of a different format. For example, let’s look at a view that returns a PNG image. To keep things simple,
we’ll just read the file off the disk:
That’s it! If you replace the image path in the
open() call with a path to a real image, you can use this very simple view to serve an image, and the browser will display it correctly.
The other important thing to keep in mind is that
HttpResponse objects implement Python’s standard file-like object API. This means that you can use an
HttpResponse instance in any place Python (or a third-party library) expects a file. For an example of how that works, let’s take a look at producing CSV with Django.
Python comes with a CSV library,
csv. The key to using it with Django is that 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:
The code and comments should be self-explanatory, but a few things deserve a mention:
- The response gets a special MIME type,
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-Dispositionheader, 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â€¦
- Hooking into the CSV-generation API is easy: Just pass
responseas the first argument to
csv.writerfunction expects a file-like object, and
HttpResponseobjects 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 large CSV files
When dealing with views that generate very large responses, you might want to consider using Django’s
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:
Using The Template System
Alternatively, you can use the Django template system to generate CSV. This is lower-level than using the convenient Python
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
for loop. Here’s an example, which generates the same CSV file as above:
This template is quite basic. It just iterates over the given data and displays a line of CSV for each row. It uses the
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; For example, generating PDFs.