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:
- Validation should fail.
- Execution Continues: The view should be re-rendered with the warning and feedback in the form.
Actual Outcome on Bad Input:
- Validation Fails (good!).
- A Flask Development Debug Page appears showing the correct ValidationError (wtforms.validators.ValidationError: Invalid Phone Number). This shouldn't happen.
- 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())