39

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.

tmthyjames
  • 1,588
  • 2
  • 22
  • 30

5 Answers5

98

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
Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
vectorfrog
  • 1,353
  • 8
  • 13
  • 22
    In python3 `StringIO` lives in the `io` module instead of the `StringIO` module: `import io; io.StringIO()` – Charlotte Apr 11 '18 at 18:46
  • Nice! Really worked for me. – igorkf Dec 27 '19 at 00:59
  • 1
    @Charlotte super appreciate the update! – Ben Dec 10 '20 at 18:11
  • @vectorfrog Does doing this auto-download the file to the client machine? – Ujjwal Mohanty Mar 22 '22 at 05:41
  • @UjjwalMohanty, yep, this will download the csv file in the client's browser. – vectorfrog Mar 23 '22 at 15:41
  • @vectorfrog Thanks for the reply and the original answer. It worked. I have another question regarding this though. Can this be done using Javascript. I need to save the data present in the html to the csv file. And I am using ajax calls in Javascript to read the html data in my python definition in app.py. I have created a button which calls the function in my JS file and transfers the data to python definition. But in this method the csv is not downloaded. Any idea what might be the issue? – Ujjwal Mohanty Mar 28 '22 at 12:57
  • @UjjwalMohanty, I've never done it before, but if you want to create the csv using browser rendered JS, you could try method at https://javascript.plainenglish.io/javascript-create-file-c36f8bccb3be – vectorfrog Mar 29 '22 at 13:43
9

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()
chfw
  • 4,502
  • 2
  • 29
  • 32
8

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
parchment
  • 4,063
  • 1
  • 19
  • 30
1

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

Oleh Novikov
  • 558
  • 5
  • 17
1

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)
Megatron
  • 15,909
  • 12
  • 89
  • 97