如何使用 Flask 创建两个相关的动态下拉列表

How to create two dependent dynamic dropdown lists using Flask

我在 Flask 中修改了以下示例代码 https://tutorial101.blogspot.com/2021/01/python-flask-dynamic-loading-of.html 以使用 SQlite 数据库。 app.py的objective是显示汽车品牌的选项,然后按品牌显示可用车型。 app.py 使用两个下拉框,用户可以从中创建 selection。原始源代码是用 Flask 编写的,用于访问 MySQL 数据库。我修改了 app.py 的代码以使用 SQlite 数据库。

app.py 运行良好。我可以用选项可视化两个下拉框。当我 select 一个汽车品牌时,该应用会使用可用型号更新第二个下拉框。

按下提交按钮时我想显示两个select离子的值。具体来说,哪个是 selected 汽车品牌,哪个是 selected 车型。

我在代码中加入了一条指令,可以显示汽车品牌的值,也可以显示对应车型的值。但是,我找不到获取 selected 汽车模型值的方法。

我怎样才能做到这一点?

谢谢。

注意:问题文本已被编辑得更具体。问题本质没变

我附上源代码和使用的模板。

app.py:

# flask sqlalchemy

from flask_sqlalchemy  import SQLAlchemy

# app.py

from flask import Flask, render_template, request, jsonify

app = Flask(__name__)
    
app.config['SECRET_KEY'] = "caircocoders-ednalan"

# sqlite config
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///testingdb.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# Bind the instance to the 'app.py' Flask application
db = SQLAlchemy(app)
    

class Carbrands(db.Model):
    __tablename__ = 'carbrands' 
    brand_id = db.Column(db.Integer, primary_key = True)
    brand_name = db.Column(db.String(250))

    def __repr__(self):
    
        return '\n brand_id: {0} brand_name: {1}'.format(self.brand_id, self.brand_name)


    def __str__(self):

        return '\n brand_id: {0} brand_name: {1}'.format(self.brand_id, self.brand_name)

class Carmodels(db.Model):
    __tablename__ = 'carmodels' 
    model_id = db.Column(db.Integer, primary_key = True)
    brand_id = db.Column(db.Integer)
    car_models = db.Column(db.String(250))

    def __repr__(self):
    
        return '\n model_id: {0} brand_id: {1} car_models: {2}'.format(self.model_id, self.brand_id, self.car_models)


    def __str__(self):

        return '\n model_id: {0} brand_id: {1} car_models: {2}'.format(self.model_id, self.brand_id, self.car_models)




# index.html
@app.route('/', methods=["POST","GET"])
def index():

    q = Carbrands.query.all()
    print(q)

    carbrands = q

    return render_template('index.html', carbrands=carbrands)


# response.html 
@app.route("/get_child_categories", methods=["POST","GET"])
def get_child_categories():
  
    if request.method == 'POST':
        parent_id = request.form['parent_id']

        car = Carbrands.query.filter_by(brand_id=parent_id).first()
        print("Car brand '{0}' parent_id '{1}'".format(car.brand_name, parent_id))
        carmodels = Carmodels.query.filter_by(brand_id=parent_id).all()
        print(carmodels)

    return jsonify({'htmlresponse': render_template('response.html', carmodels=carmodels)})


if __name__ == "__main__":
    app.run(debug=True)

index.html:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Python Flask Dynamic Loading of ComboBox using jQuery Ajax and SQlite</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" />
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
 $('#search_category_id').change(function(){
        $.post("/get_child_categories", {
         parent_id: $('#search_category_id').val()
        }, function(response){ 
                $('#show_sub_categories').html(response);
                $('#show_sub_categories').append(response.htmlresponse);
                
        });
        return false;
    });
}); 
</script>
</head>
<body>
     <div class="container">
        <div class="row">
            <div class="col-lg-2"></div>
        <div class="col-lg-8">
        <h3 align="center">Python Flask Dynamic Loading of ComboBox using jQuery Ajax and SQlite</h3>
        <form action="#" name="form" id="form" method="post">
            <div class="form-group">
                <label>Select Category</label>
                <select name="search_category"  id="search_category_id" class="form-control">
                    <option value="" selected="selected"></option>
                    {% for row in carbrands %}
                    <option value='{{row.brand_id}}'>{{row.brand_name}}</option>
                    {% endfor %}
                    </select> 
            </div>
            <div id="show_sub_categories"></div>
            <button type="submit" class="btn btn-primary">Submit</button>
        </form>
        </div>
        <div class="col-lg-2"></div>
    </div>
</div>
</body>
</html>

response.html:

<div class="form-group">
  <label>Select Sub Category</label>
  <select name="sub_category"  id="sub_category_id" class="form-control">
     <option value="" selected="selected"></option>
      {% for row in carmodels %} 
      <option value="{{row.model_id}}">{{row.car_models}}</option>
      {% endfor %}
  </select> 
</div>

我找到了解决办法。我附上源代码作为参考,以防有人对解决方案感兴趣。这个问题意味着研究工作。

从截图中可以看出,显示了选择汽车品牌和选择车型的选项

选择汽车品牌时,第二个下拉框显示汽车品牌对应的车型列表。

按下进程选择按钮时,应用程序会显示消息,指示已选择的汽车品牌和车型。

解决方案是读取包含汽车品牌和型号的数据库表。此信息将转换为 python 字典以提供给两个下拉框。选择汽车品牌时,应用程序会在第二个下拉框中显示与汽车品牌对应的车型 SEE:@app.route('/_update_dropdown')

https://api.jquery.com/jquery.getjson/ 文档对解决方案至关重要。特别是$.getJSON('/_update_dropdown',...的实现和$.getJSON('/_process_data',...的实现在Flask的很多例子中关于下拉框的动态更新,它演示了下拉框是如何实现的,但是没有提到如何获取所选数据;这对继续这个过程很重要。在这种情况下,这个解决方案包括了所有细节Flask中动态下拉框的实现及选中值的处理

在研究了可能的解决方案后,我找到了 link 一个类似的问题。因此,我研究了解决方案并根据我的需要对其进行了调整。感谢 link 的作者:How to create chained selectfield in flask without refreshing the page? 的宝贵贡献。没有那个指导,就不可能成功地解决这个问题。

app.py:

# SEE: 

# flask sqlalchemy

from flask_sqlalchemy  import SQLAlchemy
import sqlalchemy

# app.py

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

# Initialize the Flask application
app = Flask(__name__)
    
app.config['SECRET_KEY'] = "caircocoders-ednalan"

# sqlite config
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///cars.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# Bind the instance to the 'app.py' Flask application
db = SQLAlchemy(app)
    

class Carbrands(db.Model):
    __tablename__ = 'carbrands' 
    brand_id = db.Column(db.Integer, primary_key = True)
    brand_name = db.Column(db.String(250))

    def __repr__(self):
    
        return '\n brand_id: {0} brand_name: {1}'.format(self.brand_id, self.brand_name)


    def __str__(self):

        return '\n brand_id: {0} brand_name: {1}'.format(self.brand_id, self.brand_name)

class Carmodels(db.Model):
    __tablename__ = 'carmodels' 
    model_id = db.Column(db.Integer, primary_key = True)
    brand_id = db.Column(db.Integer)
    car_model = db.Column(db.String(250))

    def __repr__(self):
    
        return '\n model_id: {0} brand_id: {1} car_model: {2}'.format(self.model_id, self.brand_id, self.car_model)


    def __str__(self):

        return '\n model_id: {0} brand_id: {1} car_model: {2}'.format(self.model_id, self.brand_id, self.car_model)


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
    """

    # Create a dictionary (myDict) where the key is 
    # the name of the brand, and the list includes the names of the car models
    # 
    # Read from the database the list of cars and the list of models. 
    # With this information, build a dictionary that includes the list of models by brand. 
    # This dictionary is used to feed the drop down boxes of car brands and car models that belong to a car brand.
    # 
    # Example:
    #
    # {'Toyota': ['Tercel', 'Prius'], 
    #  'Honda': ['Accord', 'Brio']}

    carbrands = Carbrands.query.all()
    # Create an empty dictionary
    myDict = {}
    for p in carbrands:
    
        key = p.brand_name
        brand_id = p.brand_id

        # Select all car models that belong to a car brand
        q = Carmodels.query.filter_by(brand_id=brand_id).all()
    
        # build the structure (lst_c) that includes the names of the car models that belong to the car brand
        lst_c = []
        for c in q:
            lst_c.append( c.car_model )
        myDict[key] = lst_c
    
    
    class_entry_relations = myDict
                        
    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 the car brand: {} and the model: {}.".format(selected_class, selected_entry))




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

    """
    initialize drop down menus
    """

    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)

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">
        <h1 class="text-center text-muted">Dynamic dropdowns</h1>
      </div>

      <br><br><br>

      <div class="row">
        <div class="form-group col-xs-6">
          <label for="all_classes">Select a car</label>
          <select class="form-control" style="color: white; background: #34568B;" 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 a model</label>
          <select class="form-control" style="color:white; background:#009B77;" id="all_entries">
            {% for o in all_entries %}
                    <option value="{{ o }}">{{ o }}</option>
            {% endfor %}
          </select>
        </div>
      </div>

      <div>
        <button type="button" style="color:white; background:#3498DB;" 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>

create_tables_and_data.sql:

--
-- Table structure for table `carbrands`
--


CREATE TABLE CARBRANDS ( 
    BRAND_ID INT PRIMARY KEY NOT NULL,
    BRAND_NAME CHAR(250)
);


--
-- Dumping data for table `carbrands`
--

INSERT INTO CARBRANDS (BRAND_ID, BRAND_NAME) VALUES (1, 'Toyota');
INSERT INTO CARBRANDS (BRAND_ID, BRAND_NAME) VALUES (2, 'Honda');
INSERT INTO CARBRANDS (BRAND_ID, BRAND_NAME) VALUES (3, 'Suzuki');
INSERT INTO CARBRANDS (BRAND_ID, BRAND_NAME) VALUES (4, 'Mitsubishi');
INSERT INTO CARBRANDS (BRAND_ID, BRAND_NAME) VALUES (5, 'Hyundai');
--
-- Table structure for table `carmodels`
--


CREATE TABLE CARMODELS (
    MODEL_ID INT PRIMARY KEY NOT NULL,
    BRAND_ID INT NOT NULL,
    CAR_MODEL CHAR(250) NOT NULL
);


--
-- Dumping data for table `carmodels`
--

INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (1, 1, 'Toyota Corolla');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (2, 1, 'Toyota Camry');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (3, 1, 'Toyota Yaris');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (4, 1, 'Toyota Sienna');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (5, 1, 'Toyota RAV4');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (6, 1, 'Toyota Highlander');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (7, 2, 'Honda HR-V');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (8, 2, 'Honda Odyssey');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (9, 3, 'Swift');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (10, 3, 'Celerio');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (11, 3, 'Ertiga');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (12, 3, 'Vitara');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (13, 4, 'Mirage');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (14, 4, 'Mirage G4');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (15, 4, 'Xpander Cross');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (16, 4, 'Montero Sport');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (17, 4, 'Strada Athlete');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (18, 5, 'Reina ');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (19, 5, 'Accent');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (20, 5, 'Elantra');
INSERT INTO CARMODELS (MODEL_ID, BRAND_ID, CAR_MODEL) VALUES (21, 5, 'Tucson');