WTForm File Validation in Python

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
	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):
	def get(self):
		form = EasyForm()
		user_name = tornado.escape.xhtml_escape(self.current_user)


	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))