Posting a WTForm via AJAX with Flask

A questioner raised this in the Flask IRC channel, the second in as many days, figured I’d note it down for posterity. They asked how could they send some form-data to the server and do something with the returned data without causing a full refresh of the user’s current page.

Anytime you want to do something without causing a refresh, but require server-communication, we look to the magic of jQuery, specifically the AJAX functions — they can talk to a server in a background request and act on the data returned.

So lets start with a basic Flask app:

Next we’ll need to create our example.html template to display the form:

We’ve linked to jQuery in our header, as we know we’re going to need that library to handle our AJAX request.

Next we’ll flip back to Flask to add the view route that is going to receive the AJAX posted form, and return some basic JSON in response.

We’ve told our route to only expect POST’ed data, and when it receives it, to validate it against our WTF defined form, and if successful, send a little JSON dictionary back saying hello <example>. If the validation fails, then we’ll just send back the entire form.errors dictionary — this would be useful if, say, you had a DataRequired() validator on a form element so you could highlight the missing data when the user submitted the form.

The final element is the jQuery code to block the form from being POST’ed normally, grab the data from that form and send it to our /something/ view for processing:

The only complicated part is the header injection at the end. As good denizens of the Internet, we want to use CSRF protection for our form submissions, but AJAX/Flask-WTF handles that slightly differently so we need to explicitly make sure that our X-CSRFToken is set in our AJAX request.

Now when we post the form, and view the returned data in the javascript console of our browser of choice, we should see a little JSON object which contains our success message.

Extras

I’ve set the jQuery code to just send any form on the page to /something/ if you had several forms, each requiring a different destination, then you could either be super-verbose and have some AJAX code for each form based off their id such as $("#foo") in the form shown above, or (better) we could set the destination using the action attribute in the form tag, and use jQuery to read that value and use it to set the destination.

The {{ form.csrf_token._value() }} is ugly. If you look at the Flask-WTF docs, it goes through a method of having global CSRF protection, which would give your Jinja templates access to a much more eye-pleasing {{ csrf_token }} variable to clean up that eyesore.