I'm building my first web application in Python and am making the application more dynamic.
The code is working properly as long as the user is filling the correct data. However I am testing now some erroneous input and there is where it fails. I am using WTForms and while building the non-ajax pages everything was working fine, the application gave the front end a nice "Invalid input" when the user put wrong data in there. Now the application breaks.
This is the Form:
class ExpenseForm(FlaskForm):
list_id = HiddenField(validators=[DataRequired()])
title = StringField('Expense', validators=[DataRequired()])
expensetype_id = SelectField('Expense Type', coerce=int)
price = DecimalField('Cost', places=2, validators=[DataRequired()])
quantity = IntegerField('Quantity', validators=[DataRequired()])
currency_id = SelectField('Currency', coerce=int)
country_id = SelectField('Country', coerce=int)
city = StringField('City', validators=[DataRequired()])
date = DateField('Date', validators=[DataRequired()])
exceptional_cost = BooleanField('Exceptional cost')
submit = SubmitField('Add')
My route:
@bp.route('/commit_expense', methods=['POST'])
@login_required
def commit_expense():
form = ExpenseForm()
form.expensetype_id.choices = [(et.id, et.name) for et in Expensetype.query.order_by('name')]
form.currency_id.choices = [(c.id, c.short) for c in Currency.query.order_by('short')]
form.country_id.choices = [(co.id, co.name) for co in Country.query.order_by('name')]
print(form.data)
if form.validate_on_submit():
extra_add_expense(form)
return jsonify({'success': 'Expense added'})
return jsonify({'error':'Failed to add expense',
'form_errors':[{form[field].label: ', '.join(errors)} for field, errors in form.errors.items()]})
And the javascript:
$(function () {
$("#expense_form").submit(function (event) {
event.preventDefault(); // Prevent the form from submitting via the browser
var form = $(this);
var error_div = $("#form_errors");
$(error_div).children().remove();
$.ajax({
type: form.attr('method'),
url: form.attr('action'),
data: form.serialize()
}).done(function (data) {
if (data.error) {
for (item in data.form_errors) {
Object.keys(data.form_errors[item]).forEach(function (key) {
$('<p>').text(key + ': ' + data.form_errors[item][key]).addClass("show_error").appendTo(error_div);
});
};
} else {
form[0].reset();
daily_refresh();
}
}).fail(function (data) {
// Finish fail here
});
});
});
So as long as I fill in the correct information in all fields, everything passes as it should, but the moment I fill in some wrong information, for the price for example, I get this:
TypeError: key Label('price', 'Cost') is not a string
The example json message is:
list_id=2&csrf_token=IjA0NGJjNzU1Nzg3ODg1ZjhhODQ0YzE5ODMwYzkzZTBkNjEyMWQyYjIi.Du1lBg.nJJpKiNSV4pnCLsIfzUaqlsmscg&title=ff&expensetype_id=1&price=f&quantity=1¤cy_id=2&city=GHanzhaou&country_id=5&date=2018-12-08
Then I get the following data from the print(form.data):
{'list_id': '2', 'title': 'ff', 'expensetype_id': 1, 'price': None, 'quantity': 1, 'currency_id': 2, 'country_id': 5, 'city': 'GHanzhaou', 'date': datetime.date(2018, 12, 8), 'exceptional_cost': False, 'submit': False, 'csrf_token': 'IjA0NGJjNzU1Nzg3ODg1ZjhhODQ0YzE5ODMwYzkzZTBkNjEyMWQyYjIi.Du1lBg.nJJpKiNSV4pnCLsIfzUaqlsmscg'}
Now I have tested this against my other non-ajax functions and they also seem to have a 'None' when the data is incorrect (with other DecimalFields for example). At the point of validation they just return the 'DataRequired' error and the page handles the display.
I don't quite see the reason behind this failing. Any help or insight is highly appreciated.
Thanks in advance!
form[field].label
is a Label
instance, not string.
Use form[field].label.text
to get the string representation of the label.
The last line of your view should be
return jsonify({'error':'Failed to add expense',
'form_errors':[{form[field].label.text: ', '.join(errors)} for field, errors in form.errors.items()]})