The tricky thing about validating a file with WTForms, at least within Tornado, is that the file
field doesn’t get sent to the validator, or more accurately, isn’t in the field
object that does.
I had a validator for an MP3 file upload that seemed to work for a while, then it just stopped working. And there seem to have been other programmers who experienced the same issue.
I’m using WTForms and wtforms-tornado.
After spending way too long on various forums and hacks, I finally figured out to use print out the __dict__
of the field object. I used the pprint (pretty print) python module and lo and behold:
{'_translations': ,
'data': u'',
'default': None,
'description': u'',
'errors': [],
'filters': (),
'flags': ,
'id': 'mp3_file',
'label': Label('mp3_file', u'mp3_file'),
'meta': ,
'name': 'mp3_file',
'object_data': None,
'process_errors': [],
'raw_data': [],
'short_name': 'mp3_file',
'type': 'FileField',
'validators': []}
There’s almost nothing there! (meta was an empty list.)
But the self.request.files
object was available in the post method, so after instantiating the (WTForms) Form()
object, I just threw self.request.files['mp3_file']
into it’s empty raw_data
attribute:
form = EasyForm(self.request.arguments)
form.mp3_file.raw_data = self.request.files['mp3_file']
Here’s the code:
from wtforms_tornado import Form
from wtforms import ValidationError
def MpegFile(form, field):
from pprint import pprint
#pprint(field.__dict__)
filename = field.raw_data[0].filename
if not field.raw_data[0].content_type in ["audio/mpeg"]:
raise ValidationError(" must be an audio/mpeg file.")
ext = os.path.splitext(filename)[1]
if not ext.lower() in [".mp3"]:
raise ValidationError(" must be an audio/mpeg file with extension .mp3.")
class EasyForm(Form):
submitter_name = wtforms.TextField('submitter_name', validators=[wtforms.validators.DataRequired()], default=u'Your Name')
email = wtforms.TextField('email', validators=[wtforms.validators.Email(), wtforms.validators.DataRequired()])
mp3_file = wtforms.FileField(u'mp3_file', validators=[MpegFile])
comments = wtforms.TextAreaField('comments', validators=[])
class Submissionform(BaseHandler):
@tornado.web.authenticated
def get(self):
form = EasyForm()
user_name = tornado.escape.xhtml_escape(self.current_user)
self.write(templates.load("fileuploadform.html").generate(form=form))
def post(self):
form = EasyForm(self.request.arguments)
form.mp3_file.raw_data = self.request.files['mp3_file']
if form.validate():
for f in self.request.arguments:
details += "
" + self.get_argument(f, default=None, strip=False)
#self.request.files['mp3_file'] is an instance of tornado.httputil.HTTPFile
fileinfo = self.request.files['mp3_file'][0]
details += "
" + fileinfo['filename']
database.create_track(fileinfo['body'], fileinfo['filename'], self.request.arguments)
self.write(templates.load("confirm_submission.html").generate(form=form))
else:
self.write(templates.load("fileuploadform.html").generate(form=form))