简体   繁体   中英

HTML range slider to Flask with AJAX call

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM