populate WTForms select field using value selected from previous field populate WTForms select field using value selected from previous field flask flask

populate WTForms select field using value selected from previous field


Here is an example implementation of this logic to work with WTForms native functionality. The trick here, is if you want to use WTForms validation, you need to instantiate the form with every possible value, then modify the available options in Javascript to show the filtered values based on the other select.

For this example I'm going to use the concept of States and Counties (I work with a lot of geo data so this is a common implementation I build).

Here's my form, I've assigned unique IDs to the important elements to access them from Javascript:

class PickCounty(Form):    form_name = HiddenField('Form Name')    state = SelectField('State:', validators=[DataRequired()], id='select_state')    county = SelectField('County:', validators=[DataRequired()], id='select_county')    submit = SubmitField('Select County!')

Now, the Flask view to instantiate and process the form:

@app.route('/pick_county/', methods=['GET', 'POST'])def pick_county():    form = PickCounty(form_name='PickCounty')    form.state.choices = [(row.ID, row.Name) for row in State.query.all()]    form.county.choices = [(row.ID, row.Name) for row in County.query.all()]    if request.method == 'GET':        return render_template('pick_county.html', form=form)    if form.validate_on_submit() and request.form['form_name'] == 'PickCounty':        # code to process form        flash('state: %s, county: %s' % (form.state.data, form.county.data))    return redirect(url_for('pick_county'))

A Flask view to respond to XHR requests for Counties:

@app.route('/_get_counties/')def _get_counties():    state = request.args.get('state', '01', type=str)    counties = [(row.ID, row.Name) for row in County.query.filter_by(state=state).all()]    return jsonify(counties)

And, finally, the Javascript to place at the bottom of your Jinja template. I'm assuming because you mentioned Bootstrap, that you are using jQuery. I'm also assuming this is in line javascript so I'm using Jinja to return the correct URL for the endpoint.

<script charset="utf-8" type="text/javascript">$(function() {    // jQuery selection for the 2 select boxes    var dropdown = {        state: $('#select_state'),        county: $('#select_county')    };    // call to update on load    updateCounties();    // function to call XHR and update county dropdown    function updateCounties() {        var send = {            state: dropdown.state.val()        };        dropdown.county.attr('disabled', 'disabled');        dropdown.county.empty();        $.getJSON("{{ url_for('_get_counties') }}", send, function(data) {            data.forEach(function(item) {                dropdown.county.append(                    $('<option>', {                        value: item[0],                        text: item[1]                    })                );            });            dropdown.county.removeAttr('disabled');        });    }    // event listener to state dropdown change    dropdown.state.on('change', function() {        updateCounties();    });});</script>


PJ Santoro's answer is great. The update on load was called but the event listener didn't work for me at first. Turned out that I hadn't swapped out 'state' for my own field ID as I thought it was a keyword referring to the state of the field! D'oh! So in looking for other options, I found this also worked, that might be useful for somebody out there:

    // event listener to state dropdown change$('#state').change(function() {    updateCounties();});