I am new to python and programming in general.
I keep getting UploadNotAllowed error even though I set the form field validator to Optional(). My goal is to allow users the choice of uploading or not uploading a profile picture. All configurations work well when an image if selected to be uploaded.
Any help will be appreciated.
Here is the form field:
class SettingsForm(FlaskForm):
profile_pic = FileField('Profile Picture', validators= [Optional(), FileAllowed(images, 'Only images are allowed here')])
Here is my views.py:
if form.validate_on_submit():
filename = images.save(request.files['profile_pic'])
current_user.profile_pic = images.url(filename)
this is a slightly edited version from the docs (here). Which also give you some reminders about what your html file should contain.
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired
from werkzeug.utils import secure_filename
class PhotoForm(FlaskForm):
photo = FileField(validators=[Optional(), FileAllowed(images, 'Only images are allowed here')])
#app.route('/upload', methods=['GET', 'POST'])
def upload():
if form.validate_on_submit():
f = form.photo.data
filename = secure_filename(f.filename)
#you can replace this with wherever you want to save your images
f.save(os.path.join(
app.instance_path, 'photos', filename
))
current_user.profile_pic = images.url(filename)
return redirect(url_for('index'))
return render_template('upload.html', form=form)
Related
Hello,
I am trying to handle errors when uploading a file with a disallowed filetype or when submiting without selecting any file.
All I get is "flask_uploads.exceptions.UploadNotAllowed" and on the web page "Internal Server Error". I want to flash an error message but have no idea how to handle the error. I googled and I read the documentation but I couldn't find a way.
Everything works fine when selecting and submiting the right type of file/files(in this case IMAGES).
Thank you!
if request.method == 'POST':
if form.validate_on_submit() and 'artwork' in request.files:
art = form.artwork.data
this_artf = art_f+'/'+str(order_no_art)
app.config['UPLOADED_IMAGES_DEST'] = this_artf
images = UploadSet('images', IMAGES)
configure_uploads(app, images)
for image in art:
images.save(image)
As you said, you have looked in the documentation...
I just enhanced the example shown at https://github.com/jugmac00/flask-reuploaded with handling the mentioned UploadNotAllowed exception.
Be sure to not forget to import the exception first!
...
from flask_uploads.exceptions import UploadNotAllowed
...
#app.route("/", methods=['GET', 'POST'])
def upload():
if request.method == 'POST' and 'photo' in request.files:
try:
photos.save(request.files['photo'])
flash("Photo saved successfully.")
return render_template('upload.html')
except UploadNotAllowed:
flash("File type not allowed!")
return render_template('upload.html')
return render_template('upload.html')
This is a generic answer, but I am positive you can apply it to your case.
By the way, I saw you configured the app within the request handling:
if request.method == 'POST':
if form.validate_on_submit() and 'artwork' in request.files:
art = form.artwork.data
this_artf = art_f+'/'+str(order_no_art)
app.config['UPLOADED_IMAGES_DEST'] = this_artf
images = UploadSet('images', IMAGES)
configure_uploads(app, images)
While this currently works, this is not the way it is supposed to do.
It is about these lines...
app.config['UPLOADED_IMAGES_DEST'] = this_artf
images = UploadSet('images', IMAGES)
configure_uploads(app, images)
You have to move those lines outside the request context, e.g. at the top of your module or in your application factory.
Disclaimer: I am the maintainer of Flask-Reuploaded.
Handle flask_uploads.exceptions.UploadNotAllowed exception when invoking save method on instance of UploadSet.
from flask_uploads.exceptions import UploadNotAllowed
from flask import flash # make sure to configure secret key for your app
#....
error_files = []
for image in art:
try:
images.save(image)
except flask_uploads.exceptions.UploadNotAllowed as err:
error_files.append(image.filename)
continue
flash(error_files, 'error')
You could then, present the files in the rendered template by getting the flashed messages.
I'm using Flask, Flask-Bootstrap and Flask-Uploads with Python 3.7.1 to create a very simple application that accepts a csv file containing raw data.
The 'upload' page must allow only .csv files to be uploaded. I have tried to implement the answer given on this post.
Upload attempts with .csv work as expected, but other file types (eg .jpg) still appear to be accepted. Am I missing something obvious here?
'details.html' simply renders the filename on the page for now.
Python Code:
import os
from flask import Flask, render_template, url_for, request
from flask_bootstrap import Bootstrap
from flask_uploads import UploadSet, configure_uploads
app = Flask(__name__)
Bootstrap(app)
# Upload files configuration
csv_file = UploadSet('files', ('csv'))
app.config['UPLOADED_FILES_DEST'] = 'static/uploadstorage'
configure_uploads(app, csv_file)
# index
#app.route('/')
def index():
return render_template('index.html')
# if csv file, show the data in a table. if not csv file, reload index page
#app.route('/datauploads', methods=['GET', 'POST'])
def datauploads():
if request.method == 'POST' and 'csv_data' in request.files:
file = request.files['csv_data']
filename = file.filename
file.save(os.path.join('static/uploadstorage', filename))
return render_template('details.html', filename=filename)
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
You are ignoring the upload set when you accept files. You need to use the UploadSet.save() method for extension checking to kick in.
You also need to pass in a sequence of extensions, currently you pass in a string, add a comma to make it a tuple:
csv_file = UploadSet('files', ('csv',))
and in your view use:
#app.route('/datauploads', methods=['GET', 'POST'])
def datauploads():
if request.method == 'POST' and 'csv_data' in request.files:
filename = csv_file.save(request.files['csv_data'])
return render_template('details.html', filename=filename)
return render_template('index.html')
You probably want to catch the UploadNotAllowed exception, however, as you'd otherwise get a 500 error:
from flask_uploads import UploadSet, configure_uploads, UploadNotAllowed
from flask import flash
#app.route('/datauploads', methods=['GET', 'POST'])
def datauploads():
if request.method == 'POST' and 'csv_data' in request.files:
try:
filename = csv_file.save(request.files['csv_data'])
return render_template('details.html', filename=filename)
except UploadNotAllowed:
flash('Only CSV files can be uploaded, please correct', 'error')
return render_template('index.html')
I used message flashing (which Flask-Bootstrap can support directly), but your index.html could also be altered to accept an error message.
I am looking for a simple way to pre-populate fields of a form with data from a text file.
Here are my imports:
#For Flask
from flask import Flask, render_template, flash, request, redirect, url_for, send_file
#For Web Form
from wtforms import Form, TextField, TextAreaField, validators, StringField, SubmitField, SelectField, RadioField, IntegerField, DateTimeField, DecimalField, FloatField
from werkzeug.utils import secure_filename
import re #For Regex
I have the following class for my form:
class MainForm(Form):
#Regex
reEmail_comp = re.compile('^.+#.+\..+$')
reJobname_comp = re.compile('[^\s]+[\w]+$')
#Form Fields
familyname = TextField('Family Name:',
validators=[validators.DataRequired()])
firstname = TextField('First Name:',
validators=[validators.DataRequired()])
email = TextField('Email:',
validators=[validators.DataRequired(),
validators.Length(min=3, max=51),
validators.Regexp(regex=reEmail_comp)])
# Copy List of Job Templates
list_job_name = list_job_templates
# Create List of Tuples
choices_list = list(zip(list_job_name,list_job_templates))
# Generate string to be evaluate as analyses Radiofield
radio_string = ("RadioField('Analysis Type:',"
"validators=[validators.DataRequired()],choices="+str(choices_list)+")")
# Attribute value to element as a variable
locals()["analyses"] = eval(radio_string)
jobname = TextField('Job Name:',
validators=[validators.DataRequired(),
validators.Length(min=3, max=100),
validators.Regexp(regex=reJobname_comp)])
jobdescription = TextAreaField('Job Description:',
validators=[validators.Optional(),
validators.Length(min=0, max=10000)])
And the following view:
#app.route('/home', methods=['GET', 'POST'])
def home():
form = MainForm(request.form)
print(form.errors)
if request.method == 'POST':
familyname=request.form['familyname']
firstname=request.form['firstname']
email=request.form['email']
analyses=request.form['analyses']
jobname=request.form['jobname']
jobdescription=request.form['jobdescription']
if jobdescription == "":
jobdescription = "No Description"
nextpage=request.form['submit'] #Get submission button value
if form.validate():
#Print Temp file
print("||".join(["Family Name={}".format(familyname),
"First Name={}".format(firstname),"E-Mail={}".format(email),
"Analyse={}".format(analyses),"Job Name={}".format(jobname),
"Job Description={}".format(jobdescription)]),
file=open(submission_dir+"temp1", "w"))
#Redirections following which button has been clicked
if nextpage == 'Continue to Submission':
return redirect(url_for('nextpage'))
else:
return redirect(url_for('nextbis'))
else:
flash('Error: Please make sure that all the fields are correctly filled.')
return render_template('home.html', form=form)
The user has access to the form and can fill it.
As you can see in the view there is a step during which all information are stored in a temporary "temp1.txt" file once the user submits its information.
What I would like to do is to give the user the option to go back to this form in case he would like to modify an information he previously submit.
And to simplify this, I would like the form to display the same fields, but this time, pre-populated with the information found in the temporary text file from the previous submission.
How can I do that in a simple way ?
If you created a form and populated it from request, then same can be done with file values.
form = MainForm(read_file_in_correct_format())
where read_file_in_correct_format method returns object {fieldname:value}. It would be probably better to save as JSON so you can easily parse it and support more complex structures.
All you have to do is correct initialize form based on request.method
#app.route('/home', methods=['GET', 'POST'])
def home():
if request.method == 'POST':
# so if form is submitted, populate it from request
form = MainForm(request.form)
...
else:
# if we are just viewing form, then try to load stored values from temp1.txt
form = MainForm(read_file_in_correct_format())
I'm trying to save a file to disk, not in a class or anything just straight to disk but I can't for the life of me figure out how.
So far I've got:
View: `
def uploadview(request):
uploadtestvar='Uploadpage!'
request.session['test']='Is this working?'
if request.method == 'POST':
form=UploadForm(request.POST, request.FILES)
if form.is_valid():
forcetitle='test'
try:
pass
except:
pass
return HttpResponseRedirect('/test/')
else:
form=UploadForm()
return render(request, 'test/uploads.html',{'uploadtestvar':uploadtestvar, 'form':form})`
Form stolen directly from Django:
from django import forms
class UploadForm(forms.Form):
title=forms.CharField(max_length=50)
file=forms.FileField()
I've searched countless threads and found none that gives an example simple enough for me to understand how to get that request.FILES['file'] saved to disk somewhere. The possible filetypes are png, jpg, and pdf.
Pass FileSystemStorage to FileField in models where FileSystemStorage has the storage directory
from django.core.files.storage import FileSystemStorage
fs = FileSystemStorage(location = 'media/files/')
class UploadModel(models.Model):
title=forms.CharField(max_length = 50)
file=forms.FileField(storage = fs)
I keep running into this strange issue that I can't seem to figure out a solution for. I cannot copy and show all of my code in it's entirety here, but I will try to outline the general structure of my flask app to present my issue.
(Let's ignore all of the content in the /static folder and my helper modules)
I have 3 main views, let's call them viewA, viewB, and index:
viewA.html
viewB.html
index.html
viewA and viewB both display two forms, but with different content (i.e. viewA displays form1 & form2, and viewB also displays form1 & form2).
A simplified version of my script code is as follows:
#imports
from flask import Flask, render_template, session, redirect, url_for, request
from flask_wtf import FlaskForm
#etc. etc.
app = Flask(__name__)
app.config['SECRET_KEY'] = 'blah blah blah'
manager = Manager(app)
bootstrap = Bootstrap(app)
moment = Moment(app)
class FormOne(FlaskForm):
sample_field = StringField('Sample Field:')
class FormTwo(FlaskForm):
other_field = StringField('Other Field:', validators=[Required()])
submit = SubmitField('Submit')
class UploadToA(FlaskForm):
content= StringField('Content to send to view A:', validators=[Required()])
submit = SubmitField('Submit')
class UploadToB(FlaskForm):
content= StringField('Content to send to view A:', validators=[Required()])
submit = SubmitField('Submit')
#app.route('/ViewA', methods=['GET', 'POST'])
def view_a():
"""
A lot of data manipulation
"""
form1 = FormOne()
form2 = FormTwo()
if request.method == 'GET':
"""
populate forms with content
"""
if request.method == 'POST':
if form2.validate_on_submit();
"""
clear session variables
"""
return redirect(url_for('index'), code=302)
return render_template('viewA.html', form1=form1, form2=form2)
#app.route('/ViewB', methods=['GET', 'POST'])
def view_b():
"""
A lot of data manipulation
"""
form1 = FormOne()
form2 = FormTwo()
if request.method == 'GET':
"""
populate forms with content
"""
if request.method == 'POST':
if form2.validate_on_submit();
"""
clear session variables
"""
return redirect(url_for('index'), code=302)
return render_template('viewB.html', form1=form1, form2=form2)
#app.route('/', methods=['GET', 'POST'])
def index():
"""
Some data manipulation
"""
formA = UploadToA()
formB = UploadToB()
if formA.validate_on_submit()':
"""
pull content from form A
create some session variables
"""
return redirect(url_for('view_a'))
if formB.validate_on_submit()':
"""
pull content from form B
create some session variables
"""
return redirect(url_for('view_b'))
return render_template('index.html', formA=formA, formB=formB)
if __name__ == '__main__':
manager.run()
Now the issue at hand I am having here is that for some strange reason when I'm in 'viewA.html' and I submit my form, I SHOULD be redirected back to 'index.html' but for some strange reason it redirects me to 'viewB.html'. Furthermore, the opposite also holds true: when i'm in 'viewB.html' and I submit my form, I SHOULD also be redirected back to 'index.html' but it redirects me to 'viewA.html'. Yet, if I am in either viewA or viewB, I have no issues of going back to the index view if I manually enter the url into my browser.
Any ideas as to why I might be running into this issue?
Thanks in advance :)
I have finally figured out the source of my problem. It turns out that in my 'viewA.html' template file, I had the following in my < form > tag:
<form class="form form-horizontal" method="post" role="form" action="{{url_for('index')}}">
And the problem all lies in that last part:
action="{{url_for('index')}}"
As a result, everytime I would submit form2 in viewA.html it would create a post request for my index page rather than a post request for the viewA.html page (which caused a redirect to the wrong view). Thus, by simply removing the action attribute (action="{{url_for('index')}}"), I was able to solve my problem!
Since the full code isn't here, I can't confirm this for sure, but what I think is happening is this:
You open form A
You submit form A
It sends a redirect to /index
It sends a redirect to /FormB
if formB.validate_on_submit():
return redirect(url_for('view_b'))
This is probably sending a redirect to View B. Try changing that last line to something like return something_else and seeing if it sends that after submitting form A.