0

Suppose the Flask-Admin view below (note I'm using flask_wtf not wtforms). I'd like to upload a csv file, and then on the model_change, parse the csv and do some stuff to it before returning the result which will then be stored into the model. However, I get the error: TypeError: coercing to Unicode: need string or buffer, FileField found

from flask_wtf.file import FileField

class myView(ModelView):
    [...]
    def scaffold_form(self):
        form_class = super(myView, self).scaffold_form()
        form_class.csv = FileField('Upload CSV')
        return form_class

    def on_model_change(self, form, model):
        csv = form.csv
        csv_data = self.parse_file(csv)
        model.csv_data = csv_data

    def parse_file(self, csv):
        with open(csv, 'rb') as csvfile:
            data = csv.reader(csvfile, delimiter=',')

            for row in data:
                doSomething()

When accessing csv.data, I'll get <FileStorage: u'samplefile.csv' ('text/csv')> but this object doesn't actually contain the csv's data.

Chrispy
  • 1,300
  • 3
  • 11
  • 25

2 Answers2

1

Okay, after digging further into the flask_wtf module I was able to find enough to go on and get a workaround. The FileField object has a data attribute that wraps the werkzeug.datastructures.FileStorage class which exposes the stream attribute. The docs say this typically points to the open file resource, but since I'm doing this in memory in this case it's a stream buffer io.BytesIO object.

Attempting to open():

with open(field.data.stream, 'rU') as csv_data:
    [...]

Will result in an TypeError: coercing to Unicode: need string or buffer, _io.BytesIO found.

BUT, csv.reader can also take a string or buffer directly, so we pass in the straight shootin' buffer to the csv.reader:

buffer = csv_field.data.stream # csv_field is the FileField obj
csv_data = csv.reader(buffer, delimiter=',')

for row in csv_data:
    print row

I found it interesting that if you need additional coercion to/from Unicode UTF-8, the csv examples in docs provides a snippet on wrapping an encoder/decoder.

Chrispy
  • 1,300
  • 3
  • 11
  • 25
0

For me, this did the trick:

def on_model_change(self, form, model):
        tweet_file = form.tweet_keywords_file
        buffer = tweet_file.data.stream 
        file_data =  buffer.getvalue()
scottydelta
  • 1,786
  • 4
  • 19
  • 39