Flask WTForms: Why is my POST request to upload a file not sending file data?
I'm trying to make a form that uploads a file, but the file data is not being sent with the request. I am manually navigating to my file and hitting submit. My FileRequired validator fails. (If I don't include, the data
field form.scan_file
is empty.)
Here is my form:
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileAllowed, FileRequired
class ScanForm(FlaskForm):
scan_file = FileField(validators=[FileAllowed(['nii', 'nii.gz', 'zip']), FileRequired()])
Here is mine views.py
:
from flask import Blueprint, render_template, request, flash, redirect, url_for, session
from .models import Scan
from .forms import ScanForm
from .service import ScanService
from cookiecutter_mbam.utils import flash_errors
blueprint = Blueprint('scan', __name__, url_prefix='/scans', static_folder='../static')
@blueprint.route('/add', methods=['GET', 'POST'])
def add():
"""Add a scan."""
form = ScanForm(request.form)
if form.validate_on_submit():
f = form.scan_file.data
service = ScanService()
xnat_uri = service.upload(session['user_id'], session['curr_experiment'], f)
Scan.create(xnat_uri=xnat_uri)
flash('You successfully added a new scan.', 'success')
return redirect(url_for('experiment.experiments'))
else:
flash_errors(form)
return render_template('scans/upload.html',scan_form=form)
Here is mine upload.html
:
{% extends "layout.html" %}
{% block content %}
<form method="POST" action="{{ url_for('scan.add') }}" enctype="multipart/form-data">
{{ scan_form.csrf_token }}
<input type="file" name="file">
<input class="btn btn-primary" type="submit" value="Submit">
</form>
{% endblock %}
It seems I made the same mistake as this guy . What am I doing wrong?
EDIT: I've found this question since posting , but while working through the solutions provided, none seem to be relevant to my situation.
Edit 2: At one point, I printed request.files in the Werkzeug debugger, which was an empty dictionary. I can't completely rebuild what I did to get that result. Since then, I've inserted some print statements and, in fact, request.files
have my file object. So I have a way to retrieve my files. But I should be able to get back my file object form.scan_file.data
(see here ). Now, this result is None
. More specifically, it form.scan_file.has_file()
is calculated as False
. form.data
Evaluate as{'scan_file': None, 'csrf_token': <long-random-string> }
The result of this problem is that validation doesn't work, even though I have another method of retrieving the file object. My form doesn't pass FileRequired() validation.
EDIT 3: With a new understanding of the problem, I found that it is similar to this question . However, it's at least apparently not duplicated, since there isn't form = ScanForm(request.form)
, form = ScanForm()
or form = ScanForm(CombinedMultiDict((request.files, request.form)))
makes any difference in the behavior listed in Edit 2.
First, check if your data is published on that route . Second, I don't think you need to pass request.form
to ScanForm
, just instantiate it:
def add():
"""Add a scan."""
form = ScanForm()
...
To check for content posted via the form, instead of
if form.validate_on_submit():
You can use and print form.scan_file.data
:
if form.is_submitted():
print(form.scan_file.data)
Finally, you can render the input file using {{scan_form.scan_file}} or <input type="file" name="scan_file">
(the name attribute of the input element should be equal to "scan_file" ) .
Here is my example:
form:
class ArticleForm(FlaskForm):
article_image = FileField('Article_image', validators=[FileRequired()])
Form in template:
<form action="" method="post" enctype="multipart/form-data">
{{ article_form.csrf_token }}
{{ article_form.article_image }}
<input type="submit" value="submit"/>
</form>
Controller (save file):
article_form = ArticleForm()
if article_form.validate_on_submit():
f = article_form.article_image.data
name = current_user.username + "__" + f.filename
name = secure_filename(name)
f.save(os.path.join("./static/article_images/", name))