我目前正在使用 wtf 制作地址表单,其中包含国家、州、城市等。数据库全部用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'))
现在我正在尝试实现链式选择字段排序效果以优化用户体验。期望的效果是,无需离开或刷新页面即可让选择字段根据前一个选择字段提取数据。
例如,用户在“国家”中选择“澳大利亚”,则“州”的第二个选择字段应仅包含澳大利亚的州。
我对此主题进行了一些研究,但无法提出令人满意的解决方案。这是我发现的两个可能但不令人满意的解决方案。
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框架有满意的解决方案吗?
如果您想要在客户端上实现动态功能,就没有办法编写一些 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()
我最近遇到了类似的问题,并使用这个答案中的想法解决了它。
一个最小的例子可能如下所示:
因此,只有两个下拉菜单,第二个下拉菜单取决于第一个下拉菜单的选定值;页面上的所有其他元素都不受此选择的影响(这里我只使用一个复选框)。一旦做出选择,下拉菜单下方的按钮将触发一个操作,然后结果可以再次显示在页面上(这里,我只更新按钮下方的句子,当然你也可以做更合理的事情),例如到:
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)