Send a file through Django Class Based Views
django, django-class-based-views, download, python
Solution
This is a generic problem when you need to provide different responses for the same data. The point at which you would want to interject is when the context data has already been resolved but the response hasn't been constructed yet.
Class based views that resolve to the `TemplateResponseMixin` have several attributes and class methods that control how the response object is constructed. Do not be boxed into thinking that the name implies that only HTML responses or those that need template processing can only be facilitated by this design. Solutions can range from creating custom, reusable response classes which are based on the behavior of the `TemplateResponse` class or creating a reusable mixin that provides custom behavior for the `render_to_response` method.
In lieu of writing a custom response class, developers more often provide a custom `render_to_response` method on the view class or separately in a mixin, as it's pretty simple and straightforward to figure out what's going on. You'll sniff the request data to see if some different kind of response has to be constructed and, if not, you'll simply delegate to the default implementation to render a template response.
Here's how one such implementation might look like:
import csv
from django.http import HttpResponse
from django.utils.text import slugify
from django.views.generic import TemplateView
class CSVResponseMixin(object):
"""
A generic mixin that constructs a CSV response from the context data if
the CSV export option was provided in the request.
"""
def render_to_response(self, context, **response_kwargs):
"""
Creates a CSV response if requested, otherwise returns the default
template response.
"""
# Sniff if we need to return a CSV export
if 'csv' in self.request.GET.get('export', ''):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="%s.csv"' % slugify(context['title'])
writer = csv.writer(response)
# Write the data from the context somehow
for item in context['items']:
writer.writerow(item)
return response
# Business as usual otherwise
else:
return super(CSVResponseMixin, self).render_to_response(context, **response_kwargs):
Here's where you can also see when a more elaborate design with custom response classes might be needed. While this works perfectly for adding ad-hoc support for a custom response type, it doesn't scale well if you wanted to support, say, five different response types.
In that case, you'd create and test separate response classes and write a single `CustomResponsesMixin` class which would know about all the response classes and provide a custom `render_to_response` method that only configures `self.response_class` and delegates everything else to the response classes.
Problem
We use class based views for most of our project. We have run into an issue when we try and create a CSV Mixin that will allow the user to export the information from pretty much any page as a CSV file. Our particular problem deals with CSV files, but I believe my question is generic enough to relate to any file type. The problem we are having is that the response from the view is trying to go to the template (say like `from django.views.generic import TemplateView`). We specify the template in the `urls.py` file. ``` url(r'^$', MyClassBasedView.as_view(template_name='my_template.html')) ``` How can you force the response to bypass the template and just return a standard `HttpResponse`? I'm guessing you'll need to override a method but I'm not sure which one. Any suggestions? EDIT1: It appears that I was unclear as to what we are trying to do. I have rendered a page (via a class based view) and the user will see reports of information. I need to put in a button "Export to CSV" for the user to press and it will export the information on their page and download a CSV on to their machine. It is not an option to rewrite our views as method based views. We deal with almost all class based view types (DetailView, ListView, TemplateView, View, RedirectView, etc.)