Flask and WTForms - How to determine if File Upload field is submitted for processing purposes

Viewed 3778

I've created a form with an optional WTForms File field to upload a file. The form works... but after submission, my view.py code is always trying to process the uploaded file, regardless of whether it was submitted or not.

How do I determine if a file has been uploaded or not? I'd like my code to only process the upload if something was uploaded.

Right now, I haven't found the correct way to validate, so my code is handling the uploaded file, even when no file has been uploaded.

I'm currently trying this in my views.py to distinguish, but it doesn't work (see below):

    attachFile = False

    if attachment:
        attachFile = True

I've also tried the following to try to make something happen (these are commented out in the full code in views.py):

            First attempt: if form.attachment.data is not str:
            Second attempt: if not attachment.filename == "":   
            Third attempt: if (isinstance(attachment,str) == False):
            (Fourth (and current) attempt is above)

I've also tried the following, but get the following error when a file is not uploaded:

    if attachment.data:
        attachFile = True

## AttributeError: 'str' object has no attribute 'data'

forms.py:

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, 
SubmitField, TextAreaField, FileField
from wtforms.fields.html5 import EmailField
from wtforms.validators import DataRequired, Email

class MailerForm(FlaskForm):
    fromName = StringField('fromName', validators=[DataRequired()])
    fromEmail = EmailField('fromEmail', validators=[DataRequired(), Email()])
    subject = StringField('Subject', validators=[DataRequired()])
    toAddress = TextAreaField('toAddress', validators=[DataRequired()])
    message = TextAreaField('message', validators=[DataRequired()])
    attachment = FileField('attachment')
    submit = SubmitField('Send Email')

views.py

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

    # compiled regex to quick and dirty email validation
    EMAIL_REGEX = re.compile(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)")


    form = MailerForm()

    if form.validate_on_submit():
        fromName = form.fromName.data
        fromEmail = form.fromEmail.data  
        subject = form.subject.data
        toAddress = form.toAddress.data
        messageBody = form.message.data
        attachment = form.attachment.data
        newFileName = ""

        attachFile = False

        if attachment:
            attachFile = True


        basedir = os.path.abspath(os.path.dirname(__file__))

        ## lists to track successful and unsuccessful email addresses submitted
        success = []
        failure = []

        ##
        ## split email address
        ##
        addresses = toAddress.split("\n")
        ##
        ## iterate through email addresses, validate, and send
        ##
        for address in addresses:

            address = address.strip()

            if EMAIL_REGEX.match(address):

                ##if (isinstance(attachment,str) == False):
                ##if not attachment.filename == "":
                if attachFile == True:
                    filename = os.path.join(basedir + "/static/" + app.config['UPLOAD_FOLDER'], attachment.filename)

                    attachment.save(filename)


                msg = Message(subject)
                msg.sender = (fromName,fromEmail)
                msg.recipients = [address]

                msg.body = messageBody
                #if form.attachment.data is not str:
                #if not attachment.filename == "":   
                #if (isinstance(attachment,str) == False):

                if attachFile == True:
                newFileName = attachment.filename
                    with app.open_resource(filename) as fp:

                        msg.attach(
                            newFileName,
                            "application/octet-stream",
                            fp.read())

                mail.send(msg)

                success.append(address)



            else:
                failure.append(address)
                print("Failed:" + address)    

    else: 
        """Renders index page."""
        return render_template(
            'mailer/mailer.html',
        form = form
    )

    ##
    ## Successfully emailed, time to nuke the temp attachment
    ##
    os.system('rm ' + basedir + "/static/" + app.config['UPLOAD_FOLDER'] + "/'" + newFileName + "'")

    ##
    ##
    ##
    return render_template(
            'mailer/mailerCompleted.html',
            form = form,
            success = success,
            failure = failure
        )
3 Answers

You can also use not:

if not form.attachment.data:
    print('no files has been uploaded')

not A is true when A is empty or None.

So, it triggers when no files are attached(form.attachment.data == None)

Add FileRequired() to validators:

from flask_wtf.file import FileField, FileRequired
...
attachment = FileField('attachment' , validators=[FileRequired()])

File is required and form will not validate if file is not submitted.

EDIT:

If you want file to be optional, remove validators=[FileRequired()] and check if file is passed:

if form.attachment.data is None:
    print("File is empty")
    #  Code if file is empty
else:
    #  Code if file is passed

Using Flask-WTF==0.14.3, wtforms==2.3.3

The is None test does not work. The =='' test is works.

with a form definition using FileField

class ProfileForm(FlaskForm):
    # other fields
    photo = FileField('Profile photo')

when form is submitted

def profile():
    form = ProfileForm()
    if form.validate_on_submit():
        print('form.photo.data: [{}]'.format(form.photo.data))
        print('is None test: [{}]'.format(form.photo.data is None))
        print('==\'\' test: [{}]'.format(form.photo.data == ''))

        if form.photo.data is None:
            # will NOT work
            flash('you should upload a profile photo')

        if form.photo.data == '':
            # will work
            flash('profile photo is highly recommended')

    return render_template('profile.html', form=form)
Related