Flask-WTF FileField does not set data attribute to an instance of Werkzeug FileStorage
Asked Answered
H

2

11

Flask-WTF's documentation states that:

Flask-WTF provides you a FileField to handle file uploading, it will automatically draw data from flask.request.files if the form is posted. The data attribute of FileField will be an instance of Werkzeug FileStorage.

However, when I use FileField, the data attribute after posting is not a FileStorage object. Instead, it is either None (if I define my form with enctype="multipart/form-data" as suggested in the documentation) or the filename as a string (if I don't define an enctype).

Here is the relevant Jinja2 template:

{% from "_form.html" import render_field %}
{% block body %}
  <section class="page-width-container" id="offset-content">
    <div id="utility-box">
      <h1 class="utility-header">Settings</h1>
      {{ message }}
      <form action="/settings" method="post" enctype="multipart/form-data">
        {{ render_field(form.photo) }}
        <input type="submit" class="form-action" value="Save" />
      </form>
    </div>
  </section>
{% endblock %}

The _form.html macro:

{% macro render_field(field) %}
  <div class="field">
    {{ field.label }}
    <span class="form-input">{{ field()|safe }}</span>
    <div class="clear"></div>
    {% if field.errors %}
    <ul class="errors">
      {% for error in field.errors %}
      <li>{{ error }}</li>
      {% endfor %}
    </ul>
    {% endif %}
  </div>
{% endmacro %}

And here is the Python:

from flask import Blueprint, render_template, request
from flask.ext.wtf import Form
from flask.ext.wtf.file import FileField, FileRequired

settings_bp = Blueprint('settings', __name__)

class SettingsForm(Form):
  photo = FileField('Photo', validators=[
    FileRequired('no file!'),
  ])

@settings_bp.route('/settings', methods=['GET', 'POST'])
def login():
  form = SettingsForm(request.form)
  form.validate_on_submit()
  print(type(form.photo.data))
  return render_template('settings.html', form=form, message=form.photo.data)

With the enctype attribute on the form element, the printed type is NoneType; without the enctype attribute, the printed type is unicode. Neither is the FileStorage object advertised by the documentation.

Haematoid answered 5/10, 2013 at 22:23 Comment(0)
H
12

I looked into the source code (form.py) and figured out the issue: the code to replace the default data attribute with the FileStorage object is not activated if the form is initialized with a specific formdata. The formdata must be left unspecified in order for the file functionality to work. Replacing this line:

form = SettingsForm(request.form)

With this line:

form = SettingsForm()

Resolved the issue.

Haematoid answered 6/10, 2013 at 1:2 Comment(3)
Hi Thanks for your answers. I can not find the spot you refer to in the source of form.py. Do you have a bit more details ?Gyrate
@KevinLEGOFF not OP but I believe they are talking about this partIpsambul
Thanks for this answer, this problem was driving me crazyStipel
C
0

An alternative to clearing the passed form data
from form = SettingsForm(request.form) to form = SettingsForm()

is to pass both the request.form and the request.files as suggested by the flask-wtf documentation .

from flask import request
from werkzeug.datastructures import CombinedMultiDict

form = SettingsForm(CombinedMultiDict((request.files, request.form)))
Corelation answered 11/9, 2020 at 1:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.