HTML range slider to Flask with AJAX call

送分小仙女□ 提交于 2020-07-07 12:40:32

问题


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>

回答1:


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)


来源:https://stackoverflow.com/questions/62593749/html-range-slider-to-flask-with-ajax-call

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!