rduplain/flask-wtf — Issue #12: FieldList(FileField) does not follow request.files
Author: rduplain
Created: 2012-02-29 16:46:08+00:00
State: open
Labels: bug, import

When form data is passed to a FieldList, each key in the form data
dict whose prefix corresponds to the encapsulate field's name results
in the creation of an entry. This allows for the addition of new field
entries on the basis of submitted form data and thus dynamic
field creation. See:
http://groups.google.com/group/wtforms/browse_thread/thread/824e95123e38c697/4bc8cd59a84ea5f9

However, when the encaspulated field is a FileField, the FieldList
will not create entries according to the submitted form data. The
following will fail on the last assertion:

#!python

from flask import Flask, request
from StringIO import StringIO
from flaskext.wtf import Form, FieldList, TextField, FileField

app = Flask(__name__)
app.config['SECRET_KEY'] = 'SECRET'

# given this
class BrokenForm(Form):
text_fields = FieldList(TextField())
file_fields = FieldList(FileField())

text_data = [('text_fields-0', 'First input'),
('text_fields-1', 'Second input')]

file_data = [('file_fields-0', (StringIO('contents 0'), 'file0.txt')),
('file_fields-1', (StringIO('contents 1'), 'file1.txt'))]

with app.test_request_context(method='POST',
data=dict(input_data + file_data)):
assert len(request.files) # the files have been added to the
# request

f = BrokenForm(csrf_enabled=False)

if f.validate_on_submit():
assert len(text_data) == len(f.text_fields) # this succeeds
assert len(file_data) == len(f.file_fields) # this doesn't!

This occurs because there is no overlap between the fields stored in
request.files and those in request.form, and Flask-WTF only looks at
request.form. The existing MultipleFileUpload test case didn't catch
this because it sets its FieldList's min_entries to 3; this means 3
FileFields exist that happen to correspond to what's in request.files
before that data is even processed. (These are created in the final
while loop in FieldList.process(*args), wtforms/fields.py; we're
failing in the 'if formdata:' block)

A simple solution is to combine request.form and request.files when
the latter exists and have the form process the result; the first
attached patch does just that.

This seems less than ideal, though, because it removes the distinction
between forms and files imposed by Werkzeug and thus might run counter
to sane assumptions. Furthermore, it combines the two whenever
request.files is present, not just when a FieldList encapsulates a
FileField. The second patch addresses these problems by introducing a
subclass of FieldList that processes request.files when its
encapsulated field is a FileField.

The third patch provides a test that ensures FileFields are added to
FieldLists on the basis of submitted data.

Finally, this is not strictly a problem with flask-wtf, but rather a
problem that will exist with anything that's based on Werkzeug and
uses wtforms. Perhaps there ought to be a werkzeug-wtf?

---------------------------------------
- Bitbucket: https://bitbucket.org/danjac/flask-wtf/issue/12
- Originally Reported By: [Mark Williams](http://bitbucket.org/markrwilliams)
- Originally Created At: 2011-02-10 13:18:47