34
votes

I have no problems writing a CSV outside of the Flask framework. But when I try to write it from Flask, it writes to the CSV, but only on one line.

Here is the template I'm following

@app.route('/download')
def download():
    csv = """"REVIEW_DATE","AUTHOR","ISBN","DISCOUNTED_PRICE"
"1985/01/21","Douglas Adams",0345391802,5.95
"1990/01/12","Douglas Hofstadter",0465026567,9.95
"1998/07/15","Timothy ""The Parser"" Campbell",0968411304,18.99
"1999/12/03","Richard Friedman",0060630353,5.95
"2004/10/04","Randel Helms",0879755725,4.50"""
    response = make_response(csv)
    response.headers["Content-Disposition"] = "attachment; filename=books.csv"
    return response

This writes the CSV perfectly, but when I try with my code, I get one long row.

My code:

@app.route('/download')
def post(self):

    # lots of code

    csvList.append([all,my,data,goes,here])

    csvList = str(re.sub('\[|\]','',str(csvList)))  # convert to a string; remove brackets

    response = make_response(csvList)
    response.headers['Content-Disposition'] = "attachment; filename=myCSV.csv"
    return response

My output:

Nashville Physician Service Ce,Treasury Specialist,Brentwood,TN,(615) 507-1646,La Petite Academy,Afternoon Teacher Aide,Goodlettsville,TN,(615) 859-2034,Nashville Physician Service Ce,Denial Resolution Specialist,Brentwood,TN,(615) 507-1646

Thanks.

EDIT: I tried just about all the answers and they worked for the most part, but I chose vectorfrog's because it fit with what I was trying to accomplish.

5
You should probably be using the csv (docs.python.org/2/library/csv.html) module. - David K. Hess
Also, this is a more idiomatic way to create a file for download in flask: flask.pocoo.org/snippets/32 - David K. Hess
Thanks, @DavidK.Hess! I've been not only trying to learn python basics, but really trying to write idiomatic code. This definitely helps. - tmthyjames

5 Answers

83
votes

I did something like this recently, I found that I needed to first place the csv into a StringIO and then return the StringIO. If you want the csv to be a download, here's what I did:

import StringIO
import csv
from flask import make_response

@app.route('/download')
def post(self):
    si = StringIO.StringIO()
    cw = csv.writer(si)
    cw.writerows(csvList)
    output = make_response(si.getvalue())
    output.headers["Content-Disposition"] = "attachment; filename=export.csv"
    output.headers["Content-type"] = "text/csv"
    return output
8
votes

You need to add newlines. Anyway, your method of making csvs (printing the list and removing brackets from it) is not the best way to do it. Try this instead:

csvList = '\n'.join(','.join(row) for row in csvList)

Or use the csv module:

import io, csv

dest = io.StringIO()
writer = csv.writer(dest)

for row in csvList:
    writer.writerow(row)

# Now dest is a file-like object containing your csv
7
votes

One alternative::

from flask import Flask, make_response
import pyexcel as pe
import StringIO # py2.7, for python3, please use import io

app = Flask(__name__)

data = [
    ["REVIEW_DATE","AUTHOR","ISBN","DISCOUNTED_PRICE"],
    ["1985/01/21","Douglas Adams",'0345391802',5.95],
    ["1990/01/12","Douglas Hofstadter",'0465026567',9.95],
    ["1998/07/15","Timothy \"The Parser\" Campbell",'0968411304',18.99],
    ["1999/12/03","Richard Friedman",'0060630353',5.95],
    ["2004/10/04","Randel Helms",'0879755725',4.50]
]

@app.route('/download')
def download():
    sheet = pe.Sheet(data)
    io = StringIO.StringIO()
    sheet.save_to_memory("csv", io)
    output = make_response(io.getvalue())
    output.headers["Content-Disposition"] = "attachment; filename=export.csv"
    output.headers["Content-type"] = "text/csv"
    return output

if __name__ == "__main__":
    app.debug=True
    app.run()

Another alternative is to use Flask-Excel:

from flask import Flask, make_response
from flask.ext import excel

app = Flask(__name__)

data = [
    ["REVIEW_DATE","AUTHOR","ISBN","DISCOUNTED_PRICE"],
    ["1985/01/21","Douglas Adams",'0345391802',5.95],
    ["1990/01/12","Douglas Hofstadter",'0465026567',9.95],
    ["1998/07/15","Timothy \"The Parser\" Campbell",'0968411304',18.99],
    ["1999/12/03","Richard Friedman",'0060630353',5.95],
    ["2004/10/04","Randel Helms",'0879755725',4.50]
]

@app.route('/download')
def download():
    output = excel.make_response_from_array(data, 'csv')
    output.headers["Content-Disposition"] = "attachment; filename=export.csv"
    output.headers["Content-type"] = "text/csv"
    return output

if __name__ == "__main__":
    app.debug=True
    app.run()
1
votes

Since csv is just a plain text format you shoud make shure that new line separator - \n is present at the end of every line you have

1
votes

I'm not quite sure I understand your objective, but you can try the str.join() method. So, if you wanted to make a quick CSV output from a list of lists:

csvList= [['1', '2', '3'], ['4', '5', '6'], ['asdf', '7', 'eight']]
csvStrings= []
for csvLine in csvList:
    csvStrings += [",".join(csvLine)]
print "\n".join(csvStrings)