Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

File not uploading with Flask-wtforms in cookiecutter-flask app

I am having a problem getting a file upload to work in a cookiecutter-flask app (v. 0.10.1). Right now, it is not saving the file uploaded.

Cookiecutter-Flask by default installs WTForms and Flask-WTForms. I have tried adding Flask-Uploads to this but I'm not convinced that module adds anything at this point so I have uninstalled it. This is the Flask-WTF file upload documentation: http://flask-wtf.readthedocs.io/en/latest/form.html#module-flask_wtf.file

The main difference between the documentation and my app is that I seem to have information across more files, in keeping with the conventions of the cookiecutter.

In app_name/spreadsheet/forms.py:

from flask_wtf import Form
from wtforms.validators import DataRequired
from flask_wtf.file import FileField, FileAllowed, FileRequired

class UploadForm(Form):
    """Upload form."""

    csv = FileField('Your CSV', validators=[FileRequired(),FileAllowed(['csv', 'CSVs only!'])])

    def __init__(self, *args, **kwargs):
        """Create instance."""
        super(UploadForm, self).__init__(*args, **kwargs)
        self.user = None

    def validate(self):
        """Validate the form."""
        initial_validation = super(UploadForm, self).validate()
        if not initial_validation:
            return False

In app_name/spreadsheet/views.py:

from flask import Blueprint, render_template
from flask_login import login_required
from werkzeug.utils import secure_filename
from app_name.spreadsheet.forms import UploadForm
from app_name.spreadsheet.models import Spreadsheet
from app_name.utils import flash, flash_errors

blueprint = Blueprint('spreadsheet', __name__, url_prefix='/spreadsheets', static_folder='../static')

@blueprint.route('/upload', methods=['GET', 'POST']) #TODO test without GET since it won't work anyway
@login_required
def upload():
    uploadform = UploadForm()
    if uploadform.validate_on_submit():
        filename = secure_filename(form.csv.data.filename)
        uploadform.csv.data.save('uploads/csvs/' + filename)
        flash("CSV saved.")
        return redirect(url_for('list'))
    else:
        filename = None
    return render_template('spreadsheets/upload.html', uploadform=uploadform)

This is the command line output showing no errors when I upload a file:

 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [04/Sep/2016 10:29:10] "GET /spreadsheets/upload HTTP/1.1" 200 -
127.0.0.1 - - [04/Sep/2016 10:29:10] "GET /_debug_toolbar/static/css/toolbar.css?0.3058158586562558 HTTP/1.1" 200 -
127.0.0.1 - - [04/Sep/2016 10:29:14] "POST /spreadsheets/upload HTTP/1.1" 200 -
127.0.0.1 - - [04/Sep/2016 10:29:14] "GET /_debug_toolbar/static/css/toolbar.css?0.3790246965220061 HTTP/1.1" 200 -

For the uploads/csvs directory I have tried absolute and relative paths and the directory is permissioned 766.

The template file is:

{% extends "layout.html" %}
{% block content %}
    <h1>Welcome {{ session.username }}</h1>

    {% with uploadform=uploadform  %}
        {% if current_user and current_user.is_authenticated and uploadform %}
            <form id="uploadForm" method="POST" class="" action="{{ url_for('spreadsheet.upload') }}" enctype="multipart/form-data">
              <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
              <div class="form-group">
                {{ uploadform.csv(class_="form-control") }}
              </div>
              <button type="submit" class="btn btn-default">Upload</button>
            </form>
        {% endif %}
    {% endwith %}

{% endblock %}

Which generates this HTML:

        <form id="uploadForm" method="POST" class="" action="/spreadsheets/upload" enctype="multipart/form-data">
          <input type="hidden" name="csrf_token" value="LONG_RANDOM_VALUE"/>
          <div class="form-group">
            <input class="form-control" id="csv" name="csv" type="file">
          </div>
          <button type="submit" class="btn btn-default">Upload</button>
        </form>
like image 520
Alison S Avatar asked Sep 04 '16 15:09

Alison S


People also ask

How do I upload files to the flask app?

Python for web development using Flask Handling file upload in Flask is very easy. It needs an HTML form with its enctype attribute set to 'multipart/form-data', posting the file to a URL. The URL handler fetches file from request. files[] object and saves it to the desired location.

What is WTForms in flask?

WTForms is a Python library that provides flexible web form rendering. You can use it to render text fields, text areas, password fields, radio buttons, and others. WTForms also provides powerful data validation using different validators, which validate that the data the user submits meets certain criteria you define.

Where does flask save uploaded files?

The server-side flask script fetches the file from the request object using request. files[] Object. On successfully uploading the file, it is saved to the desired location on the server. The uploaded file is saved to the temporary directory of the server for a while before it is saved to some desired location.


1 Answers

Looking through the documentation, the link you provided indicates that the data field of csv is an instance of werkzeug.datastructures.FileStorage. The documentation for FileStorage.save() suggests that:

If the destination is a file object you have to close it yourself after the call.

Could it be that because you aren't closing the file, it isn't being written to disk?

like image 101
kfb Avatar answered Oct 11 '22 15:10

kfb