【问题标题】:How to create chained selectfield in flask without refreshing the page?如何在不刷新页面的情况下在烧瓶中创建链式选择字段?
【发布时间】:2014-11-16 16:51:31
【问题描述】:

我目前正在使用 wtf 处理地址表单,其中包含 Country、State、City..etc。数据库全部用FK设置。

class Country(db.Model):
    __tablename__ = 'countries'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    users = db.relationship('User', backref='countries', lazy='dynamic')
class City(db.Model):
    __tablename__ = 'cities'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    countries_id = db.Column(db.Integer, db.ForeignKey('countries.id')) 

现在我正在尝试实现链式选择字段排序效果以优化用户体验。想要的效果是不离开或刷新页面,让selectfield根据之前的selectfield拉取数据。

例如,用户在 Country 中选择了澳大利亚,那么 State 的第二个选择字段应该只包含澳大利亚的州。

我已经对此主题进行了一些研究,但无法提出令人满意的解决方案。这是我找到的两个可能但不令人满意的解决方案。

1.使用 jQuery 插件,例如链式的。但是,此插件需要逐行编码。如果我采用这个解决方案,至少还有 400 多行,这不是很 Pythonic。例如:

<select id="series" name="series">
  <option value="">--</option>
  <option value="series-3" class="bmw">3 series</option>
  <option value="series-5" class="bmw">5 series</option>
  <option value="series-6" class="bmw">6 series</option>
  <option value="a3" class="audi">A3</option>
  <option value="a4" class="audi">A4</option>
  <option value="a5" class="audi">A5</option>
</select>

2.使用 Wtf 的“使用动态选择值选择字段”,这也是不可取的,因为它只根据前一个选择字段的默认值提取数据一次。例如:如果国家/地区的默认选择字段是澳大利亚,则州选择字段将仅包含澳大利亚境内的州。当您将国家/地区选择字段更改为美国时,州选择字段仍将仅包含澳大利亚境内的州。以下是 wtf 文档中列出的此方法的教程:

class UserDetails(Form):
    group_id = SelectField(u'Group', coerce=int)

def edit_user(request, id):
    user = User.query.get(id)
    form = UserDetails(request.POST, obj=user)
    form.group_id.choices = [(g.id, g.name) for g in Group.query.order_by('name')]

根据上述研究,我认为令人满意的解决方案应该介于两者之间,并且肯定应该涉及一些 Javascript 来监视客户端活动,而无需向服务器发送请求。有人对flask框架有满意的解决方案吗?

【问题讨论】:

    标签: jquery python flask jinja2 flask-wtforms


    【解决方案1】:

    我最近遇到了类似的问题,用this answer的思路解决了。

    一个最小的例子可能如下所示:

    所以,只有两个下拉菜单,其中第二个取决于第一个的选定值;页面上的所有其他元素都不受此选择的影响(这里我只使用一个复选框)。一旦做出选择,下拉菜单下方的按钮将触发一个动作,然后结果可以再次显示在页面上(这里,我只更新按钮下方的句子,但当然你也可以做更合理的事情),例如到:

    HTML 文件如下所示(位于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>
    

    而对应的python脚本如下:

    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)
    

    【讨论】:

    • 我正在使用您的示例,但我无法让网络刷新。我已经调试了每一步,但找不到问题。任何提示(没有太多上下文)?我在 _update_dropdown() 的控制台上得到了 200 分。
    • @Picarus:我刚刚从上面复制并粘贴了代码,它对我来说很好用。我使用 python 3.6.5 和烧瓶 1.0.2。你在控制台中得到什么样的输出?当我加载页面时我得到:127.0.0.1 - - [17/Jan/2019 08:40:37] "GET / HTTP/1.1" 200 -,然后一旦我选择了一些东西:127.0.0.1 - - [17/Jan/2019 08:40:46] "GET /_update_dropdown?selected_class=class1 HTTP/1.1" 200 -。更改 class 时会看到什么?
    • 感谢您的及时回复。我没有检查您的代码,而是将您的代码添加到我的一些现有代码中。仍然不确定问题是什么,但知道有效。除了您的代码中包含的版本之外,我还使用了其他 jquery 版本(更新)。并不是说这是问题所在,但现在工作正常。
    • @Picarus:很高兴它现在可以工作了!确实,jquery 和 bootstrap 都应该更新。
    【解决方案2】:

    如果您想在客户端上进行动态处理,则无法编写一些 JavaScript。幸运的是,这不是您估计的 400 多行。这个例子没有使用 WTForms,但这并不重要。关键部分是以 JSON 格式发送可用选项,并动态更改可用选项。这是一个单文件可运行的 Flask 应用程序,演示了基本思想。

    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()
    

    【讨论】:

      猜你喜欢
      • 2022-06-19
      • 1970-01-01
      • 2017-05-11
      • 2019-11-25
      • 2017-12-21
      • 2011-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多