Flask WTForms: Why is my POST request to upload a

2019-02-19 08:39发布

I am trying to make a form to upload a file, but the file data is not being sent with the request. I'm manually navigating to my file and hitting submit. My FileRequired validator fails. (And if I don't include it the data field on form.scan_file is empty.)

Here's 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's my 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's my 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 doesn't look like I'm making the same mistake as this person. What am I doing wrong?

EDIT: Since posting, I have found this question, but on working through the offered solutions, none seem relevant to my situation.

EDIT 2: At one point, I printed request.files in the Werkzeug debugger and it was an empty dict. I can't reconstruct exactly what I did to get that result. Since then, I've inserted some print statements and in fact, request.files has my file object. So I have a way to retrieve my file. But I am supposed to be able to retrieve my file object at form.scan_file.data (see here). Right now this evaluates to None. More specifically, form.scan_file.has_file() evaluates to False. form.data evaluates to {'scan_file': None, 'csrf_token': <long-random-string> }

Even if I have another way of retrieving my file object, a consequence of this problem is that validation isn't working. My form doesn't pass the FileRequired() validation.

EDIT 3: With my new understanding of my problem, I see that it's similar to this question. However, it's at least apparently not duplicative because none of form = ScanForm(request.form), form = ScanForm(), or form = ScanForm(CombinedMultiDict((request.files, request.form))) make any difference to the behavior outlined in Edit 2.

1条回答
干净又极端
2楼-- · 2019-02-19 09:14

First of all, check if your data gets POST request on that route. Second, I think you don't need to pass request.form to ScanForm, you just need to instantiate it like:

def add():
    """Add a scan."""
    form = ScanForm()
    ...

To check what gets posted with 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)

Lastly, you can render input file with {{scan_form.scan_file }} or <input type="file" name="scan_file"> (name attribute of 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 (saving 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))

EDIT:

I can't comment still beucase of low reputation, but I'm 90% sure the problem is "Lastly, you can render input file with {{scan_form.scan_file }} or (name attribute of input element should be equal to "scan_file")". Just put name attribute to be "scan_file".

查看更多
登录 后发表回答