File not uploading with Flask-wtforms in cookiecutter-flask app
<p>I am having a problem getting a file upload to work in a <a href="https://github.com/sloria/cookiecutter-flask">cookiecutter-flask</a> app (v. 0.10.1). Right now, it is not saving the file uploaded.</p>

<p>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: <a href="http://flask-wtf.readthedocs.io/en/latest/form.html#module-flask_wtf.file">http://flask-wtf.readthedocs.io/en/latest/form.html#module-flask_wtf.file</a></p>

<p>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.</p>

<p>In <code>app_name/spreadsheet/forms.py</code>:</p>

<pre><code>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
</code></pre>

<p>In <code>app_name/spreadsheet/views.py</code>:</p>

<pre><code>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)
</code></pre>

<p>This is the command line output showing no errors when I upload a file:</p>

<pre><code> * 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 -
</code></pre>

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

<p>The template file is:</p>

<pre><code>{% extends "layout.html" %}
{% block content %}
    &lt;h1&gt;Welcome {{ session.username }}&lt;/h1&gt;

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

{% endblock %}
</code></pre>

<p>Which generates this HTML:</p>

<pre><code>        &lt;form id="uploadForm" method="POST" class="" action="/spreadsheets/upload" enctype="multipart/form-data"&gt;
          &lt;input type="hidden" name="csrf_token" value="LONG_RANDOM_VALUE"/&gt;
          &lt;div class="form-group"&gt;
            &lt;input class="form-control" id="csv" name="csv" type="file"&gt;
          &lt;/div&gt;
          &lt;button type="submit" class="btn btn-default"&gt;Upload&lt;/button&gt;
        &lt;/form&gt;
</code></pre>

<p>Looking through the documentation, the link you provided indicates that the <code>data</code> field of <code>csv</code> is an instance of <code>werkzeug.datastructures.FileStorage</code>. The documentation for <a href="http://werkzeug.pocoo.org/docs/0.11/datastructures/#werkzeug.datastructures.FileStorage.save" rel="nofollow"><code>FileStorage.save()</code></a> suggests that:</p>

<blockquote>
  <p>If the destination is a file object you have to close it yourself after the call. </p>
</blockquote>

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

python flask flask-wtforms flask-uploads