Easy autocomplete for Flask WTForms (with jQuery UI)

30/09/2018 0 By walsk

What it takes to make it work — checklist.

I’m using the following stack:

  • Flask (with WTForms plugin)
  • SQLAlchemy
  • jQuery UI Autocomplete plugin

I’m going to make autocompleting country field — kind of a classic example.

Step by step guide to create a simple form with autocompletion

Add code for new table, that will store countries data:

class Country(db.Model):
    __tablename__ = 'country'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), unique=True, nullable = False)
    isoa2 = db.Column(db.String(6), unique=True, nullable = False)
    isoa3 = db.Column(db.String(3), unique=True, nullable = False)

After upgrading the db (flask db migrate -m "country table" and flask db upgrade to get the new table), you add some new data into it (I’ve added a few countries manually through cli).

Add route for data retrieval:

@login_required
@app.route('/countries', methods=['GET'])
def get_countries():
    res = Country.query.all()
    countries = [r.as_dict() for r in res]
    return jsonify(countries)

Create a form class (descendant of FlaskForm):

class TestForm(FlaskForm):
    country = StringField('Country', id='country_ac', validators=[DataRequired(),
    Length(max=40)],render_kw={"placeholder": "country"})

Note the id! This will be the id our jQuery selector will be connecting to! If you forget to add this, autocompletion will just quietly fail, because jQuery won’t find the element to connect. country_ac stands for “country_autocompletion”. 🙂

Then create a template. Mine looks like this: 

{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}

{% block scripts %}
{{ super() }}
<script src="{{ url_for('static', filename='js/form_autocompletes.js') }}"></script>
{% endblock %}

{% block app_content %}
<form class="form-inline">
    <div class="form-group">
        {{ form.country(class="form-control") }}
    </div>
    <button type="submit" class="btn btn-info">Submit</button>
</form>
<div id="result"></div>
{% endblock %}

A few important things to notice here:

  1. in base template I’ve added import of jQuery UI library. If it’s not there, JS code (will show it later) will fail saying “autocomplete is not a function”. This template is importing that.
  2. form_autocompletes.js is my js code, which I will show later. What’s really important: please import autocompleting code after the super() function call (that goes up the inheritance tree and loads all the js it finds basically). Otherwise this will also fail, because won’t find required libraries.

Then create js for autocompletion:

$(document).ready(function(){
    var countries=[];

    function loadCountries(){
        $.getJSON('/countries', function(data, status, xhr){
            for (var i = 0; i < data.length; i++ ) {
                countries.push(data[i].name);
            }
        });
    };

    loadCountries();
    $( "#country_ac" ).autocomplete({
        source: countries
    });
});

See the #country_ac? This selector points exactly to our country field! And that works fine. (Just tested.)

This setup works well for me and autocompletes field values.