I have a HTML control panel with various buttons and sliders. All of the buttons are operational and when clicked send a post request which my Python app receives and then executes functions.
I cannot seem to get the slider controls to work, When I adjust the slider I get the following error:
werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
KeyError: 'button'
This is promising as at least I can see it trying to do something and failing, which is the best result achieved thus far. My javascript and AJAX knowledge is limited and I need a solution for the slider to POST request on any onchange value that python can then interpret.
I initially had the following 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>
This threw up the same error.
This is my current code that also throws the same error:
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);
}
});
}
Python:
@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)
Any support would be greatly appreciated as I'm struggling to find solutions on the web along with me aforementioned js/ajax knowledge.
Thanks
EDIT:
This is a recreation of the problem:
python:
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>
Problem is because $('form').serialize()
sends only values from <input>
but not from <button>
but in Flask you check request.form['button']
and it raises error because key button
doesn't exists in dictionary request.form
.
You have to use
request.form.get('button')
and then it returns None
when button
doesn't exists in dictionary
BTW:
you have only @app.route("/", ...)
so AJAX has to send to /
, not to /index.html
using print(volume)
after return...
is useless becuse return
ends function
in Flask you can use return jsonify(dict)
instead of return json.dumps(dict)
and then it sends special headers and JavaScript converts it to object and you can get response.volume
. Using json.dumps(dict)
it sends it as html/text and you would have to use JSON.parse(response).volume
Working code in one file (using template_render_string
instead of template_render
) so everyone can easily test it.
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)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.