HTML 范围滑块到 Flask 与 AJAX 调用



我有一个带有各种按钮和滑块的HTML控制面板。所有按钮都是可操作的,单击时发送我的 Python 应用程序接收的 post 请求,然后执行函数。

我似乎无法让滑块控件工作,当我调整滑块时出现以下错误:

werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
KeyError: 'button'

这是有希望的,因为至少我可以看到它试图做某事并失败,这是迄今为止取得的最好结果。我的 javascript 和 AJAX 知识有限,我需要一种解决方案,让滑块对 python 可以解释的任何 onchange 值进行 POST 请求。

我最初有以下 HTML:

<input id="slider" type="range" min="0" max="100" value="50" onchange="updateVolume(getElementById('slider').value); return false;">

Javascript:

<script>
function updateVolume (vol) {
$.ajax({
method: "POST",
url: '{{ url_for('main.control_panel', video_id=video.id) }}',
data: { volume: vol}
})
.done(function( msg ) {
// optional callback stuff if needed
// alert( "Data Saved: " + msg );
});
}
</script>

这引发了同样的错误。

这是我当前的代码,也抛出相同的错误:

.HTML:

<input id="slide" type="range" min="1" max="100" step="1" value="10" name="slide">
<div id="sliderAmount"></div>

Javascript:

var slide = document.getElementById('slide'),
sliderDiv = document.getElementById("sliderAmount");
slide.onchange = function() {
sliderDiv.innerHTML = this.value;
$.ajax({
url: '{{ url_for('main.control_panel', video_id=video.id) }}',
data: $('form').serialize(),
type: 'POST',
success: function(response){
console.log(response);
},
error: function(error){
console.log(error);
}
});
}

蟒:

@main.route("/control_panel/<int:video_id>", methods=['GET', 'POST'])
def control_panel(video_id):
video = video.query.get_or_404(video_id)
if request.method == 'POST':
... #IF ELIF for various button functions here 
volume = request.form['slider']
return json.dumps({'volume': volume})
return render_template('/control_panel.html', title=video.video, video=video)

任何支持将不胜感激,因为我正在努力在网络上找到解决方案以及我前面提到的 js/ajax 知识。

谢谢

编辑:

这是问题的重现:

蟒:

from flask import Flask, request, redirect, url_for, render_template, json
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def control_panel():
if request.method == 'POST':
if request.form['button'] == 'button-play':
print("play button pressed")
elif request.form['button'] == 'button-exit':
print("exit button pressed")
elif request.form['slider']:
volume = request.form['slider']
return json.dumps({'volume': volume})
print(volume)
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)

.HTML:

<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<title>Slider</title>
</head>
<body>
<div class="container" id="control_panel_1">
<form action="/" method ="post" enctype="multipart/form-data" id="form">
<div class="row">
<div class="col">
<button class="btn btn-primary" button type="submit" name="button" value="button-play">PLAY</button>
<button class="btn btn-primary" button type="submit" name="button" value="button-exit">EXIT</button>
<input id="slide" type="range" min="1" max="100" step="1" value="10" name="slide">
<div id="sliderAmount"></div>
</div>
</div>
</form>
<!--- SCRIPTS --->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
</body>
<script>
var slide = document.getElementById('slide'),
sliderDiv = document.getElementById("sliderAmount");
slide.onchange = function() {
sliderDiv.innerHTML = this.value;
$.ajax({
url: '/index.html',
data: $('form').serialize(),
type: 'POST',
success: function(response){
console.log(response);
},
error: function(error){
console.log(error);
}
});
}
</script>
</html>

问题是因为$('form').serialize()只发送来自<input>的值,而不是来自<button>的值,但在 Flask 中您检查request.form['button']并引发错误,因为字典request.form中不存在键button

你必须使用

request.form.get('button')

然后当字典中不存在button时返回None

<小时 />

顺便说一句:

  • 你只有@app.route("/", ...)所以 AJAX 必须发送到/,而不是/index.html

  • return ...之后使用print(volume)是无用的,因为return结束函数

  • 在 Flask 中,您可以使用return jsonify(dict)而不是return json.dumps(dict)然后它发送特殊的标头,JavaScript 将其转换为对象,您可以获得response.volume。使用json.dumps(dict)它将其作为html/文本发送,您必须使用JSON.parse(response).volume


在一个文件中工作代码(使用template_render_string而不是template_render(,因此每个人都可以轻松测试它。

from flask import Flask, request, redirect, url_for, render_template, json, render_template_string, jsonify
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def control_panel():
print('request.form:', request.form)

if request.method == 'POST':
if request.form.get('button') == 'button-play':
print("play button pressed")
elif request.form.get('button') == 'button-exit':
print("exit button pressed")
elif request.form.get('slide'):
volume = request.form.get('slide')
print('volume:', volume)
#return jsonify({'volume': volume})
return json.dumps({'volume': volume})
print('render')
return render_template_string('''<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<title>Slider</title>
</head>
<body>
<div class="container" id="control_panel_1">
<form action="/" method ="post" enctype="multipart/form-data" id="form">
<div class="row">
<div class="col">
<button class="btn btn-primary" button type="submit" name="button" value="button-play">PLAY</button>
<button class="btn btn-primary" button type="submit" name="button" value="button-exit">EXIT</button>
<input id="slide" type="range" min="1" max="100" step="1" value="10" name="slide">
<div id="sliderAmount"></div>
</div>
</div>
</form>
<!--- SCRIPTS --->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
</body>
<script>
var slide = document.getElementById('slide'),
sliderDiv = document.getElementById("sliderAmount");
slide.onchange = function() {
sliderDiv.innerHTML = this.value;
$.post({
url: '/',
data: $('form').serialize(),
success: function(response){
alert(response);
alert(response.volume);             // works with jsonify()
alert(JSON.parse(response).volume); // works with json.dumps()
console.log(response);
},
error: function(error){
alert(response);
console.log(error);
}
});
}
</script>
</html>''')
if __name__ == '__main__':
app.run(debug=True, use_reloader=False)

最新更新