Downloading a csv file in django

Thanks everyone for your suggestions. I picked a few new tricks :) However I think I have found the answer to my problem here: Downloading CSV via AJAX My "savefile" function is called via an Ajax request and it appears that ajax has a limitation where"save as dialog box" does not appear no matter what the HTTP headers are.

I should have mentioned that I use Ajax to call this function but it never occurred to me that this could be an issue.:) Thankyou StackOverflow!


If the file is static (i.e not generated specifically for this request) you shouldn't be serving it through django anyway. You should configure some path (like /static/) to be served by your webserver, and save all the django overhead.

If the file is dynamic, there are 2 options:

  1. Create it in memory and serve it from django.
  2. Create it on the disk, and return a HttpResponseRedirect to it, so that your webserver deals with the download itself (if the file is very large, you should use this option).

As for serving it dynamically, I've been using the following code (which is a simplified version of ExcelResponse)

import StringIO
from django.db.models.query import ValuesQuerySet, QuerySet

class CSVResponse(HttpResponse):

  def __init__(self, data, output_name='data', headers=None, encoding='utf8'):

    # Make sure we've got the right type of data to work with
    valid_data = False
    if isinstance(data, ValuesQuerySet):
        data = list(data)
    elif isinstance(data, QuerySet):
        data = list(data.values())
    if hasattr(data, '__getitem__'):
        if isinstance(data[0], dict):
            if headers is None:
                headers = data[0].keys()
            data = [[row[col] for col in headers] for row in data]
            data.insert(0, headers)
        if hasattr(data[0], '__getitem__'):
            valid_data = True
    assert valid_data is True, "CSVResponse requires a sequence of sequences"

    output = StringIO.StringIO()
    for row in data:
        out_row = []
        for value in row:
            if not isinstance(value, basestring):
                value = unicode(value)
            value = value.encode(encoding)
            out_row.append(value.replace('"', '""'))
        output.write('"%s"\n' %
                     '","'.join(out_row))            
    mimetype = 'text/csv'
    file_ext = 'csv'
    output.seek(0)
    super(CSVResponse, self).__init__(content=output.getvalue(),
                                        mimetype=mimetype)
    self['Content-Disposition'] = 'attachment;filename="%s.%s"' % \
        (output_name.replace('"', '\"'), file_ext)

To use it, just use return CSVResponse(...) passing in a list of lists, a list of dicts (with same keys), a QuerySet, a ValuesQuerySet


Did you try specifying the content-type? e.g.

response['Content-Type'] = 'application/x-download';

Edit:

Note, this code successfully triggers a "Save As" dialog for me. Note I specify "application/x-download" directly in the mimetype argument. You also might want to recheck your code, and ensure your file path is correct, and that FileWrapper() isn't doing something weird.

def save_file(request):
    data = open(os.path.join(settings.PROJECT_PATH,'data/table.csv'),'r').read()
    resp = django.http.HttpResponse(data, mimetype='application/x-download')
    resp['Content-Disposition'] = 'attachment;filename=table.csv'
    return resp