Nataku62
Nataku62

Reputation: 43

Uploading Files with Flask

I am building a form in Flask that has 2 FileFields in the form, however, I am unable to upload the files unless I use wtf.quick_form to render my html page.

I have tried to place the file upload into a class so it can be re-used, however, the field data is passed as a string.

My code:

from flask import Flask, render_template, flash, request
from flask_bootstrap import Bootstrap
from flask_wtf import FlaskForm
from wtforms import FileField, SubmitField
import wtforms.validators

from flask import Flask, request, url_for, redirect
from werkzeug.utils import secure_filename

import os

class ChangeForm(FlaskForm):

    bizAuth = FileField('Additional Sign-off:')
    newDoc = FileField('Training/Documentation')
    submit = SubmitField('Save')


@app.route('/change', methods=['GET','POST'])
def form():

    if chgForm.validate_on_submit():

        fUp = File_Upload() # File Upload class - detailed below.
        chgForm = ChangeForm()

        bizAuth = chgForm.bizAuth.data
        chgDetail['bizAuth'] = fUp.upload(bizAuth)

        newDoc = chgForm.newDoc.data
        chgDetail['newDoc'] = fUp.upload(newDoc)


class File_Upload():

    def allowed_files(self, fileName):

        ALLOWED_EXTENSIONS = self.allowed_files_lookup()

        return '.' in fileName and fileName.rsplit('.',1)[1].lower() in ALLOWED_EXTENSIONS

    def upload(self, fileName):

        f = fileName.filename

        if f == '':
            return 'NULL'

        elif f and self.allowed_files(f):

            file = secure_filename(f)
            fileName.save(os.path.join(config['src_dir'],'Normal Changes Docs\\' + f))

        return str(os.path.join(config['src_dir'],'Normal Changes Docs\\' + f))

My HTML:

 <!DOCTYPE html>
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="{{ url_for('static', filename='chgForm-hiding.css') }}">

    {% block title %}Change Process{% endblock %}

</head>

 <body>

{% block page_content %}
    <form>
        <div class="Biz_Auth">
            {{form.bizAuth.label}}
            {{form.bizAuth}}
        </div>

        <div class="new-doc">
            {{form.newDoc.label}}
            {{form.newDoc}}
        </div>
        {{form.submit}}
    </form>
{% endblock %}

</body>

If anyone can help I would be very grateful. Many of the examples I have seen don't separate the upload function out in the manner I have, and rely on using quick_form to render the page or specific html tags hardcoded into their upload function.

Upvotes: 3

Views: 3413

Answers (1)

George J Padayatti
George J Padayatti

Reputation: 898

The following changes needed to be made :

Change 1

if request.method == "POST" and chgForm.validate_on_submit():
    bizAuth = chgForm.bizAuth.data
    chgDetail['bizAuth'] = fUp.upload(bizAuth)

    newDoc = chgForm.newDoc.data
    chgDetail['newDoc'] = fUp.upload(newDoc)

Change 2

<form action="{{ url_for('form') }}" method="post" enctype="multipart/form-data">

Change 3

Add the following in the form tag, {{ form.csrf_token }}

Below is a sample python script with above changes,

app.py

from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import FileField, SubmitField
from werkzeug.utils import secure_filename
from flask import request
import os

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = './uploads'
app.config['SECRET_KEY'] = 'catchmeifyoucan'


class ChangeForm(FlaskForm):
    bizAuth = FileField('Additional Sign-off:')
    newDoc = FileField('Training/Documentation')
    submit = SubmitField('Save')


@app.route('/change', methods=['GET','POST'])
def form():
    fUp = FileUpload()  # File Upload class - detailed below.
    chgForm = ChangeForm()
    chgDetail = dict()

    # Change 1
    if request.method == "POST" and chgForm.validate_on_submit():
        bizAuth = chgForm.bizAuth.data
        chgDetail['bizAuth'] = fUp.upload(bizAuth)

        newDoc = chgForm.newDoc.data
        chgDetail['newDoc'] = fUp.upload(newDoc)

    return render_template('index.html', form=chgForm)


class FileUpload():

    def allowed_files(self, file_name):
        allowed_extensions = ['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']
        return '.' in file_name and file_name.rsplit('.',1)[1].lower() in allowed_extensions

    def upload(self, file):
        file_name = file.filename
        if file_name == '':
            return 'NULL'

        elif file_name and self.allowed_files(file_name):
            secure_file_name = secure_filename(file_name)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'],'Normal Changes Docs\\' + secure_file_name))

        return str(os.path.join(app.config['UPLOAD_FOLDER'],'Normal Changes Docs\\' + secure_file_name))


if __name__ == "__main__":
    app.run(debug=True)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Sample</title>
</head>
<body>
<form action="{{ url_for('form') }}" method="post" enctype="multipart/form-data">
    {{ form.csrf_token }}
    {{ form.bizAuth.label }}
    {{ form.bizAuth }}
    {{ form.newDoc.label }}
    {{ form.newDoc }}
    {{ form.submit }}
</form>
</body>
</html>

I hope this helps.

Upvotes: 2

Related Questions