【问题标题】:File not uploading with Flask-wtforms in cookiecutter-flask app文件未在 cookiecutter-flask 应用程序中使用 Flask-wtforms 上传
【发布时间】:2019-04-30 13:41:05
【问题描述】:

我在上传文件以在 cookiecutter-flask 应用程序(v. 0.10.1)中工作时遇到问题。现在,它没有保存上传的文件。

Cookiecutter-Flask 默认安装 WTForms 和 Flask-WTForms。我曾尝试向其中添加 Flask-Uploads,但我不相信该模块此时会添加任何内容,因此我已将其卸载。这是Flask-WTF文件上传文档:http://flask-wtf.readthedocs.io/en/latest/form.html#module-flask_wtf.file

文档和我的应用程序之间的主要区别在于,我似乎拥有跨更多文件的信息,这符合 cookiecutter 的约定。

在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

在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)

这是我上传文件时显示没有错误的命令行输出:

 * 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 -

对于uploads/csvs 目录,我尝试了绝对路径和相对路径,并且该目录的权限为 766。

模板文件为:

{% 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 %}

生成此 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>

【问题讨论】:

    标签: python flask flask-wtforms flask-uploads


    【解决方案1】:

    查看文档,您提供的链接表明csv 的data 字段是werkzeug.datastructures.FileStorage 的一个实例。 FileStorage.save() 的文档表明:

    如果目标是文件对象,您必须在调用后自行关闭它。

    可能是因为您没有关闭文件,因此没有将其写入磁盘?

    【讨论】:

      【解决方案2】:

      试试这个:

      from flask import request
      
      if uploadform.validate_on_submit():
          if 'csv' in request.files:
              csv = request.files['csv']
              csv.save('uploads/csvs/' + csv.filename)
      

      【讨论】:

        【解决方案3】:

        你的问题的主要原因在这里:

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

        所以在validate 类的validate 方法中。

        让我们快速调查一下这里发生了什么。

        在views.py 排队:

        if uploadform.validate_on_submit():
        

        flask_wtf 包调用validate 方法。所以再看看你覆盖的方法:

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

        这里有什么问题?如果initial_validation 是True,您的validate 方法将返回None。那么应该怎么办呢?只有html渲染:

        def upload():
            uploadform = UploadForm()
            if uploadform.validate_on_submit(): # <--- here it's None
                filename = secure_filename(form.csv.data.filename)
                uploadform.csv.data.save('uploads/csvs/' + filename)
                flash("CSV saved.")
                return redirect(url_for('list'))
            else:                               # <--- so this block runs
                filename = None
            # And your app will only render the same view as when using HTTP GET on that method
            return render_template('spreadsheets/upload.html', uploadform=uploadform)
        

        所以如果不需要覆盖validate方法,那就去掉它,如果是,再调整它返回True:

        def validate(self):
            """Validate the form."""
            initial_validation = super(UploadForm, self).validate()
            if not initial_validation:
                return False
            return True # <-- this part is missing
        

        当然你可以使用缩短的,我认为更合适的版本:

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

        【讨论】:

          【解决方案4】:

          在我看来,上传文件有一种更简单的方法。 这是我实现的,希望对你有帮助。由于您当前的需求看起来与我的相似,您的解决方案看起来有点复杂。

          所以我想制作一个 pdf 上传器页面,这就是我所做的。

          1. 转到config.py文件或者你定义sql数据库链接的地方
          UPLOAD_FOLDER = r'C:\location\app\upload'
          ALLOWED_EXTENSIONS = {'pdf'}
          
          1. 转到您的视图或路线并编写此内容,它会检查上传的文件是否符合扩展要求。
          def allowed_file(filename):
             return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
          
          1. 然后, 我在这里所做的是我制作了一种将文件名存储在数据库表中的方法。当我调用一个函数时,它会在文件夹中查找该特定文件名并检索并显示给我。
          @app.route("/#route details here", methods=['GET', 'POST'])
          def xyz():
          
              if request.method == 'POST': 
                  if 'file' not in request.files:
                      flash(f'No file part', 'danger')
                      return redirect(request.url)
          
                  file = request.files['file']
          
                  if file.filename == '':
                      flash(f'No selected file', 'danger')
                      return redirect(request.url)
          
                  if file and allowed_file(file.filename): #allowed file is the definition i created in point 2. 
                      filename = secure_filename(file.filename)
                      file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) #save file in a target folder.
          
                      new_report = Report(report_name=filename, report_welder_wps_association_id=report_id) #create a database entry with exact filename
          
                      db.session.add(new_report)
                      db.session.commit()
          
                      return redirect(url_for(#redirection on success condition))
          
              return render_template(#render template requirements go here)
          
          1. 最后是在我请求时获取文件的视图。 我只是查询我的数据库,获取文件名并将其重定向到此视图,并将文件名作为参数,然后它会从目标文件夹中吐出文件。
          @app.route('/upload/<filename>')
          def uploaded_file(filename) -> object:
              return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
          

          这是我需要定义的唯一形式:

          class XYZ(db.Model):
              __tablename__ = 'xyz'
          
              uploaded_file_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
              uploaded_file_name = db.Column(db.String(300), nullable=False)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-11-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-07-21
            相关资源
            最近更新 更多