Download CSV files via CSVResponseMixin

Providing csv files to people is annoying, but writing a custom mixin for use with your views can make it super easy. Often times we have to use a custom function, of some sort, and rejigger around how our views work to get a csv file, then set header information, and finally return a response.

There is a Better Way

Below is a custom mixin you can write, or use, to return a csv file based on a list of lists.

class CSVResponseMixin(object):
    csv_filename = 'csvfile.csv'

    def get_csv_filename(self):
        return self.csv_filename

    def render_to_csv(self, data):
        response = HttpResponse(content_type='text/csv')
        cd = 'attachment; filename="{0}"'.format(self.get_csv_filename())
        response['Content-Disposition'] = cd

        writer = csv.writer(response)
        for row in data:
            writer.writerow(row)

        return response

The first few lines of code are simple. We are declaring the mixin, setting the filename, and returning the filename based on a method on the mixin. That leaves us with the render_to_csv method which takes a lsit of lists, and gives us a downloadable file.

Line by Line Explanation

response = HttpResponse(content_type='text/csv')

We are creating a Django HttpResponse object which django returns, and we are setting the content type to a csv file so the browser knows what it is.

response['Content-Disposition'] = 'attachment; filename="{0}"'.format(self.get_csv_filename())

We are setting the content disposition to be an attachment so that it forces the browser to download the file. Otherwise the browser might render it out into the browser, as text. We also are setting the filename from the get_csv_filename method call instead of using the property for a couple of reasons. First, we can override it in our implementation to be more dynamic. Second, we can mock it out in our testing.

writer = csv.writer(response)
for row in data:
    writer.writerow(row)

return response

Finally, we are creating a csv writer and using our response object. Then as we loop over our list of lists we write them out one at a time generating our csv data. From there we return our response object which causes the browser to download the csv file of data.

Example Implementation

A quick and dirty example of how to do this is quite simple. All you have to do is attach this to a url pattern, and you are good to go.

class DataView(CSVResponseMixin, View):

    def get(self, request, *args, **kwargs):
        data = (('hello', 'world'), ('foo', 'bar'))
        return self.render_to_csv(data)

The telling thing about this example is we are using the View class as our base class so it isn't some fancy generic class based view. This gives it simplicity people are after with function based views, but is more understandable and testable.

Conclusion

Just like with LastAccessMixin we are leveraging good OOP practice to give us generic functionality we want to use. This one gives us a standard way to export data as a csv file in a consistent way. If you want to use this mixin plus many other useful ones checkout django-braces. They currently have a lot of good ones, and this one will be coming in a future release.

comments powered by Disqus