How to create chained selectfield in flask without refreshing the page?

喜夏-厌秋 提交于 2019-12-03 00:50:46

If you want something dynamic on the client, there's no way around writing some JavaScript. Luckily it's not the 400+ lines you estimate. This example doesn't use WTForms, but that's not really important. The key part is sending the available choices as JSON, and dynamically changing the available options. Here's a single file runnable Flask app that demonstrates the basic idea.

from flask import Flask, render_template_string

app = Flask(__name__)

@app.route('/')
def index():
    systems = {
        'PlayStation': ['Spyro', 'Crash', 'Ico'],
        'N64': ['Mario', 'Superman']
    }

    return render_template_string(template, systems=systems)

template = """
<!doctype html>
<form>
    <select id="system">
        <option></option>
    </select>
    <select id="game"></select>
    <button type="submit">Play</button>
</form>
<script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
    "use strict";

    var systems = {{ systems|tojson }};

    var form = $('form');
    var system = $('select#system');
    var game = $('select#game');

    for (var key in systems) {
        system.append($('<option/>', {'value': key, 'text': key}));
    }

    system.change(function(ev) {
        game.empty();
        game.append($('<option/>'));

        var games = systems[system.val()];

        for (var i in games) {
            game.append($('<option/>', {'value': games[i], 'text': games[i]}));
        }
    });

    form.submit(function(ev) {
        ev.preventDefault();
        alert("playing " + game.val() + " on " + system.val());
    });
</script>
"""

app.run()

I had a similar problem recently and solved it using the idea from this answer.

A minimal example could look like this:

So, just two dropdowns, whereby the second depends on the selected value of the first one; all other elements on the page are not affected by this selection (here I only use one check box). Once the selection is made, the button below the dropdowns will trigger an action and the result can then again be displayed on the page (here, I only update the sentence below the button but of course you can also do more reasonable stuff), e.g. to:

The HTML file looks like this (located in templates/index.html):

<!DOCTYPE html>
<html lang="en">
  <head>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
  </head>
  <body>
    <div class="container">
      <div class="header">
        <h3 class="text-center text-muted">Dynamic dropdowns</h3>
      </div>

      <div>
        Check me and I don't change status when you select something below
      </div><br>
      <div>
        <input class="form-check-input" type="checkbox" value="" id="use_me">Select me
      </div><br><br>

      <div class="row">
        <div class="form-group col-xs-6">
          <label for="all_classes">Select a class</label>
          <select class="form-control" id="all_classes">
            {% for o in all_classes %}
                    <option value="{{ o }}">{{ o }}</option>
            {% endfor %}
          </select>
        </div>
        <div class="form-group col-xs-6">
          <label for="all_entries">Select an entry</label>
          <select class="form-control" id="all_entries">
            {% for o in all_entries %}
                    <option value="{{ o }}">{{ o }}</option>
            {% endfor %}
          </select>
        </div>
      </div>

      <div>
        <button type="button" id="process_input">Process selection!</button>
      </div><br><br>
      <div id="processed_results">
        Here we display some output based on the selection
      </div>
    </div>
    <script src="https://code.jquery.com/jquery-1.12.4.js" type="text/javascript"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <script type="text/javascript">
      $(document).ready(function() {

        $('#all_classes').change(function(){

          $.getJSON('/_update_dropdown', {
            selected_class: $('#all_classes').val()

          }).success(function(data) {
                $('#all_entries').html(data.html_string_selected);
           })
        });
        $('#process_input').bind('click', function() {

            $.getJSON('/_process_data', {
                selected_class: $('#all_classes').val(),
                selected_entry: $('#all_entries').val(),


            }).success(function(data) {
                $('#processed_results').text(data.random_text);
            })
          return false;

        });
      });
    </script>
  </body>
</html>

And the corresponding python script looks as follows:

from flask import Flask, render_template, request, jsonify
import json

# Initialize the Flask application
app = Flask(__name__)


def get_dropdown_values():

    """
    dummy function, replace with e.g. database call. If data not change, this function is not needed but dictionary
    could be defined globally
    """

    class_entry_relations = {'class1': ['val1', 'val2'],
                             'class2': ['foo', 'bar', 'xyz']}

    return class_entry_relations


@app.route('/_update_dropdown')
def update_dropdown():

    # the value of the first dropdown (selected by the user)
    selected_class = request.args.get('selected_class', type=str)

    # get values for the second dropdown
    updated_values = get_dropdown_values()[selected_class]

    # create the value sin the dropdown as a html string
    html_string_selected = ''
    for entry in updated_values:
        html_string_selected += '<option value="{}">{}</option>'.format(entry, entry)

    return jsonify(html_string_selected=html_string_selected)


@app.route('/_process_data')
def process_data():
    selected_class = request.args.get('selected_class', type=str)
    selected_entry = request.args.get('selected_entry', type=str)

    # process the two selected values here and return the response; here we just create a dummy string

    return jsonify(random_text="you selected {} and {}".format(selected_class, selected_entry))


@app.route('/')
def index():

    """
    Initialize the dropdown menues
    """

    class_entry_relations = get_dropdown_values()

    default_classes = sorted(class_entry_relations.keys())
    default_values = class_entry_relations[default_classes[0]]

    return render_template('index.html',
                           all_classes=default_classes,
                           all_entries=default_values)


if __name__ == '__main__':

    app.run(debug=True)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!