Reputation: 43
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
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