简体   繁体   中英

pass a dictionary from javascript function to flask python function: always a None

We're creating a web application using Flask. On one html page, users answer some questions (using <form> ). During this process, they are asked to click a button on the page for a few times when they receive random popup notifications. In the .html file, we write javascript functions to save the timestamps of users clicking on the button into an array.

We hope to store both the user's answers and the timestamps of clicking the button. Now from .html to .py , we pass answers using request.form and pass the array of timestamps using ajax json ( request.json ). The request.form works very well but the request.json always gives a None . We have tried request.get_json() , request.get_json(force=True) , request.get_json(silent=True) , but none of them works.

Our codes are like the following:

in trial.py :

from flask import (Blueprint, flash, g, redirect, render_template, request, url_for, jsonify, json)
from werkzeug.exceptions import abort
import csv
from flaskr.db import get_db

bp = Blueprint('trial', __name__, url_prefix='/trial')

@bp.route('', methods=('GET', 'POST'))
def trial():
    if request.method == 'POST':
        if "Q1" in request.form:
            ## this part retrieves data from request.form 
            ## skip the details in the following
            db = get_db()
            db.execute(
                    'INSERT INTO trial (...)', (...))
            db.commit()

        timestamps = request.json
        print(timestamps)  ### This is always a None

   return render_template('study/trial.html')

In trial.html :

{% extends 'base.html' %}

{% block header %}
  <h1>{% block title %}Trial{% endblock %}</h1>
{% endblock %}

{% block content %}

  <!-- Skip non-related details in the following --> 
  <button class="NotificationButton" onclick="popup()">I Got A Notification</button>

  <div>
    <form name="TrialQuestions" id="TrialQuestions" method="post">
      <!-- skip the details of the questions and input for answers -->                                             
    </form>
  </div>

  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
  <script>window.jQuery || document.write('<script src="{{url_for('static', filename='jquery.js') }}">\x3C/script>')</script>
  
  <script>
    var timestamps = {"trial":[]};

    function playAudio() { 
      <!-- skip non-related details -->
      timestamps["trial"].push(remaining);
    } 

    function popup(){
      <!-- skip non-related details -->
      timestamps["trial"].push(remaining);
    }

    function timeUp(){
      document.TrialQuestions.submit();
      $.ajax({
        type: "POST",
        url: "{{ url_for("trial.trial") }}",
        contentType: "application/json",
        data: JSON.stringify(timestamps),
        dataType: "json",
        success: function(response) {
        console.log(response);
        },
        error: function(err) {
        console.log(err);
        }
      });
      alert("Time Is Up! Your Answers Have Been Submitted!");
    }

  </script>
{% endblock %}

The console.log(response); in trial.html works well, but the timestamps = request.json in trial.py always gives a None . We have searched and tried the suggestions from the following posts but none of them solves the problem:

  1. Passing Javascript array in Python Flask
  2. Get the data received in a Flask request
  3. How to send information from javascript to flask route

Can anyone give some hints? Thanks a lot!

Your form submit results in the page being reloaded and the template being transmitted again as html from the server.

An asynchronous javascript request is only ended when the callback for success or error is called. The transmission of the data is not waited for.

function timeUp() {
  console.log("sending data to " + "{{ url_for('trial.trial') }}")

  $.ajax({
    type: "POST",
    url: "{{ url_for('trial.trial') }}",
    contentType: "application/json",
    data: JSON.stringify(timestamps),
    dataType: "json",
    success: function(response) {
      console.log(response);
      // The data has now been successfully transferred.
      
      // document.TrialQuestions.submit();
    },
    error: function(err) {
      console.log(err);
    }
  });
}

When specifying the data type, the query expects exactly this as an answer. See also here .
For this reason, I am returning a response in json format.

from flask import render_template, request, jsonify

@bp.route('/', methods=('GET', 'POST'))
def trial():
    if request.method == 'POST':
        print('got a post request')

        if request.is_json: # application/json
            # handle your ajax request here!
            print('got json:', request.json)
            return jsonify(success=True) # answer as json
        else: 
            # handle your form submit here!
            print(request.form)
    return render_template('trial.html')

AJAX has the advantage that you can send and load data sets without reloading and rendering the whole page.

Within a transmission, the data is formatted in a predefined format. When you submit a form you use the type "application/x-www-form-urlencoded" or "multipart/form-data". For JSON it is "application/json".
Depending on the selected format, either the query "request.json" returns a "None" or "request.form" is an empty dictionary.

The structure of your data set depends on your requirements.
The following is a suggestion that is similar to the original approach of a form.

function timeUp() {
  const data = {
    "data": $("form[name='TrialQuestions']").serializeArray().map(elem => {
      return [elem["name"], elem["value"]];
    }),
    "ts": timestamps,
  };

  $.ajax({
    type: "POST",
    url: "{{ url_for('trial.trial') }}",
    contentType: "application/json",
    data: JSON.stringify(data),
    dataType: "json",
    success: function(response) {
      console.log(response);
    },
    error: function(err) {
      console.log(err);
    }
  });
}

Should you just want to send a form, for example because you want to do a redirect. Add one or more hidden fields to the form and fill in their value before submitting.

<form name="TrialQuestions" method="post">
  <label for="q1">Question 1</label>
  <input name="q1" id="q1" value="a1" />
  <input name="ts" type="hidden" />
</form>

<script>
  function timeUp() {
    document.forms["TrialQuestions"]["ts"].value = timestamps.join();
    document.forms["TrialQuestions"].submit();
  }
</script>

... update to the question from myself.

As posted in the question, request.form can work but request.json will give a "None".

Following Detlef's approach, according to what I tried on my side, request.json can work but request.form will give a "None". Please correct me if I mistaken anything.

So, now I propose a trade-off approach: package the form and the timestamps into one ajax JSON object. The code is now like this:

  <div>
    <form name="TrialQuestions" id="TrialQuestions">  
      <label for="Q1">Question 1</label>
      <input name="Q1" id="Q1"> 
      <!-- skip the questions -->                                              
    </form>
  </div>

  <script>
  var TrialQuestionAnswers = [];
  var timestamps = [];
  var formAndTimestamps = [];
  
  function timeUp(){
      TrialQuestionAnswers = [document.getElementById("Q1").value, ..., document.getElementById("Q13").value];
      formAndTimestamps.push(TrialQuestionAnswers);
      formAndTimestamps.push(timestamps);

      $.ajax({
        type: "POST",
        url: "{{ url_for("trial.trial") }}",
        contentType: "application/json",
        data: JSON.stringify(formAndTimestamps),
        dataType: "json",
        success: function(response) {
        console.log(response);
        },
        error: function(err) {
        console.log(err);
        }
      });
      alert("Time Is Up! Your Answers Have Been Submitted!");
    }
  </script>

It can work, but maybe this is not the most efficient solution. Better idea is much appreciated!

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