Home > front end >  Flask & WTForms: Execution Stops improperly on ValidationError
Flask & WTForms: Execution Stops improperly on ValidationError

Time:07-14

I'm using a WTForm with a custom validator the check for properly formatted phone numbers, care of the helpful package phonenumbers (https://pypi.org/project/phonenumbers/).

During testing, Form Validation fails when an improperly formatted number is passed - this is good.

Example: (111)111-1111 -> This should fail, and does!

What isn't good is that execution stops rather than returning the ValidationError so I can display it in the form.

Simplified Example:

PhoneForm

class PhoneForm(FlaskForm):
    phone_number = StringField('Phone Number', validators=[DataRequired(), Length(max=15)])
    country_code = HiddenField('')

    def validate_phone_and_country(self):
        try:
            print(f"phone no: {self.phone_number.data}")
            print(f"country: {self.country_code.data}")
            p = phonenumbers.parse(self.phone_number.data, self.country_code.data)
            
            if not phonenumbers.is_valid_number(p):
                raise ValueError()

        except (phonenumbers.phonenumberutil.NumberParseException, ValueError):
            raise ValidationError('Invalid Phone Number')
        
        return True

Validation Code

# POST data via wtform

phone_form = PhoneForm()
error = False

if phone_form.validate_phone_and_country():
   phone = PhoneRecord()
   phone_form.populate_obj(phone)
   db.session.add(phone)

 try:
    db.session.commit()
 except Exception as e:
    db.session.rollback()
    flash('There was a problem saving your information. Please try again.', 'danger')
    error = True
    
else:
    error = True

if error:
    flash('The phone number you entered was invalid. Please try again.', 'warning')
    return render_template("phone_form.html", phone)
else:
    flash('Phone number successfully changed.', 'success')
    return redirect(url_for('user.home'))

Expected Outcome on Bad Input:

  1. Validation should fail.
  2. Execution Continues: The view should be re-rendered with the warning and feedback in the form.

Actual Outcome on Bad Input:

  1. Validation Fails (good!).
  2. A Flask Development Debug Page appears showing the correct ValidationError (wtforms.validators.ValidationError: Invalid Phone Number). This shouldn't happen.
  3. Execution Stops.

Any guidance on what I'm missing here would be helpful.

Cheers!

CodePudding user response:

Your doing

phone_form = PhoneForm()
error = False

if phone_form.validate_phone_and_country():
   phone = PhoneRecord()
   phone_form.populate_obj(phone)
   db.session.add(phone)

however i think you need to do change validate_phone_and_country() to validate_on_submit()

eg. sth from my recent project:

    if registration_form.validate_on_submit():
        print(registration_form.is_submitted())
        # ----------------- Database Stuff ----------------------------------
        # create hashed password from password form

CodePudding user response:

I think the problem arises from the fact that you are calling the custom validator directly.
In the following example, the function for validation is assigned to the associated field using the name. This is called by the validate function of the form, which in turn is called by validate_on_submit. This checks whether it is a POST request and whether the input is valid.
In addition, the custom validator ensures that the country code corresponds to the number. If you enter the number with the country code in front of it, this could deviate from the entry in the corresponding field.
The number is then formatted uniformly.

from flask import (
    Flask,
    render_template,
    request
)
from flask_wtf import FlaskForm
from wtforms import HiddenField, StringField
from wtforms.validators import DataRequired, ValidationError
import phonenumbers
from phonenumbers.phonenumberutil import (
    NumberParseException,
    region_code_for_number
)

app = Flask(__name__)
app.secret_key = 'your secret here'

class PhoneForm(FlaskForm):
    country_code = HiddenField('Country Code',
        validators=[
            DataRequired()
        ]
    )
    phone_number = StringField('Phone Number',
        validators=[
            DataRequired()
        ]
    )

    def validate_phone_number(self, field):
        try:
            pn = phonenumbers.parse(field.data, self.country_code.data)
            self.country_code.data = region_code_for_number(pn) or self.country_code.data
            if not phonenumbers.is_valid_number(pn):
                raise ValueError('Invalid Phone Number')
            field.data = phonenumbers.format_number(pn, phonenumbers.PhoneNumberFormat.E164)
        except (NumberParseException, ValueError) as exc:
            raise ValidationError('Invalid Phone Number') from exc


@app.route('/', methods=['GET', 'POST'])
def index():
    form = PhoneForm(request.form)
    if form.validate_on_submit():
        print(f'CC: {form.country_code.data}\nPN: {form.phone_number.data}')
        # ...
    return render_template('index.html', **locals())
  • Related