Ok!! I'm building a flask web app, and I want to use Ajax to send some json data
Here is my code!! for the HTML and js :
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<title>Check your Grades</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="{{ url_for('static', filename='js/bootstrap3-typeahead.min.js')}}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/index.js')}}"></script>
<link rel="stylesheet" href="{{url_for('static', filename='css/style.css')}}">
</head>
<body>
<link href='https://fonts.googleapis.com/css?family=Lato' rel='stylesheet' type='text/css'>
<form id="predict-form" method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<p> welcome to grade predictor app,</p>
<p>Dear Plateform,</p>
<p>je viens
<label for="SCHOOL_RIGHT"> de </label>
<input class="typeahead" type="text" name="SCHOOL_RIGHT" id="SCHOOL_RIGHT" minlength="3" placeholder="(votre ecole de provenance)" data-provide="typeahead" autocomplete="off" required> et </p>
<p>dans
<label for="OPTION_RIGHT">l'option</label>
<input class="typeahead"
name="OPTION_RIGHT" id="OPTION_RIGHT" data-provide="typeahead" placeholder="(choisissez votre option )" required>
</p>
<p>j'ai obtenu
<label for="DIPPERC"></label>
<input type="number" name="DIPPERC" min="50" max="100" id="DIPPERC" placeholder="(Poucentage du
diplome )" required> % à l\'exetat
</p>
<p>
<button type="submit">
<svg version="1.1" class="send-icn" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="100px" height="36px" viewBox="0 0 100 36" enable-background="new 0 0 100 36" xml:space="preserve">
<path d="M100,0L100,0 M23.8,7.1L100,0L40.9,36l-4.7-7.5L22,34.8l-4-11L0,30.5L16.4,8.7l5.4,15L23,7L23.8,7.1z M16.8,20.4l-1.5-4.3
l-5.1,6.7L16.8,20.4z M34.4,25.4l-8.1-13.1L25,29.6L34.4,25.4z M35.2,13.2l8.1,13.1L70,9.9L35.2,13.2z" />
</svg>
<small>send</small>
</button>
</p>
</form>
<script >
var csrf_token = "{{ csrf_token() }}";
// this will send a token each time before a session started
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrf_token);
}
}
});
//submit form data
$("form#predict-form").submit(function(e){
console.log("form submitted")
e.preventDefault();
var data = {
}
var Form = this;
//Gathering the Data
//and removing undefined keys(buttons)
$.each(this.elements, function(i, v){
var input = $(v);
data[input.attr("name")] = input.val();
delete data["csrf_token"];
delete data["undefined"];
});
data["DIPPERC"] = data["DIPPERC"]/100.0
//Form Validation goes here....
//Save Form Data........
$.ajax({
cache: false,
url : "{{url_for('predict')}}",
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
data : JSON.stringify(data),
success : function(callback){
//Where $(this) => context == FORM
console.log("data sucessfuly submitted")
console.log(JSON.parse(callback));
}
,
error : function(){
console.log('erroor')
}
});
})
</script>
</body>
</html>
I've tried everything possible but still getting the 400 error!
I've checked all related questions but nothing.
But my research shows that the 400 error can be caused by this according to :
from here it's said that :
The HTTP 400 Bad Request response status code indicates that the server could not understand the request due to invalid syntax. The client should not repeat this request without modification.
Csrf token missing this I've already added the token , and try to send the data to the route with csrf.exempt but nothing
ContentType and applicationType not set (already do it but nothing)
Already check in the official flask documentation they say : (from )
I'm sending correct json data
So what may cause this problem ???
here is my simple view function
#predictions.route('/predictions/predict/', methods=['GET', 'POST'])
def predict():
if request.method == 'POST':
print "hello----------------------"
print request.method
print request.get_json(force=True)
return "done "
Note in my test when I send directly data via python to my route it works with this code :
def test_1_can_connect_post(self):
"""
Test API can create a (POST request)
"""
new_student = {
'DIPPERC':0.60, 'SCHOOL_RIGHT':'itfm/bukavu', 'OPTION_RIGHT':'elec indust'
}
res = self.client().post('predictions/predict/', data=json.dumps(new_student), content_type='application/json')
self.assertEqual(res.status_code, 201)
Ps: I'm sure that I'm missing a little thing but don't know what, maybe something wrong with ajax asynchronous .....
You don't need to convert your data into string: remove the JSON.stringify:
$.ajax({
cache: false,
url : "{{url_for('predict')}}",
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
data : data,
success : function(callback){
//Where $(this) => context == FORM
console.log("data sucessfuly submitted")
console.log(JSON.parse(callback));
},
error : function(){
console.log('erroor')
}
});
With JSON.stringify you convert your data into a string, so you post a string, no JSON.
Ok , after 7 days of debugging i just found a solution for my problem :
i do two things:
from now and for my future web development i will stop using jquery and I'm sure that soon i will found good reason for that ,
so for my code i decide to use plain javascript and here is the code i use to send the request :
var csrf_token = "{{ csrf_token() }}";
// this will send a token each time before a session started
var form = document.getElementById("predict-form");
form.onsubmit = function (e) {
// stop the regular form submission
e.preventDefault();
// collect the form data while iterating over the inputs
var formEntries = new FormData(form).entries();
var data = Object.assign(...Array.from(formEntries, ([x,y]) => ({[x]:y})));
delete data["csrf_token"];
data["DIPPERC"] = data["DIPPERC"]/100.0
console.log(data);
// construct an HTTP request
var xhr = new XMLHttpRequest();
xhr.open(form.method, form.action, true);
xhr.setRequestHeader("X-CSRFToken", csrf_token);
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
// send the collected data as JSON
xhr.send(JSON.stringify(data));
xhr.onloadend = function () {
console.log('blallala ')
};
};
this was for the javascript part and I was sure that i was sending good json object to my server
i went here at flask official documentation and found these 2 lines of attributes of flask request object
is_json :
Indicates if this request is JSON or not. By default a request is considered to include JSON data if the mimetype is application/json or application/*+json.
and
data
Contains the incoming request data as string in case it came with a mimetype Flask does not handle
and change my backend code to this :
#predictions.route('/predictions/predict/', methods=['GET', 'POST'])
def predict():
"""
the main methode use to predict
"""
if request.method == 'POST':
print "hello----------------------"
print request.is_json
print request.data
print '-----------------------------------------'
return "done "
And VOILA!!!! get the 200 status code and the data as a python dict.
Related
Basically I a comparing two texts one input is a pdf, another basic text.
I have created index html and within is another html for a bit clarity. using python FLASK feature.
All the code runs separately, all mentioned errors for invalid input run very fine together, its just the main result output, I presume I am messing up somehow in calling and executing the main function because all the alerts and file checks are working , and the logic inside of flask is working too.
My html file with ajax and form :
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
function submitForm() {
// Show the loading screen
document.getElementById("loading").style.display = "block";
// Get the input values
var JD = document.getElementById("JD").value;
var file = document.getElementById("FL").file[0];
// Check if a file has been selected and is a pdf
if (!file || file.type !== 'application/pdf') {
alert("Please select a valid PDF file");
return;
}
// Check if file size is less than 5 MB
if (file.size > 5000000) {
alert("File size should not exceed 5 MB");
return;
}
// Create a FormData object to send both the file and the jd to the API
var formData = new FormData($('#upload_form')[0]);
formData.append("FL", FL);
formData.append("JD", JD);
// Make the API call
$.ajax({
url: base_url + "api/Analyze",
type: "POST",
data: formData,
processData: false,
contentType: false,
success: function(result) {
// Hide the loading screen
document.getElementById("loading").style.display = "none";
alert(result);
},
error: function () {
// Hide the loading screen
document.getElementById("loading").style.display = "none";
// Display the error
alert("Error during API call");
}
});
}
</script>
</head>
<body>
<h2>Analysis</h2>
<form id="upload_form" enctype="multipart/form-data">
<p>
<label for="JD">Description:</label>
<textarea name = "JD" id="JD" rows="4" cols="50"></textarea>
</p>
<p>
<label for="FL">FILE:</label>
<input type="file" name="FL" id="FL" accept="application/pdf">
</p>
<p>
<input type="button" value="Submit" onclick="submitForm()">
</p>
</form>
<div id="loading" style="display: none;">Loading...</div>
And this is the app.py flask file -
from flask import Flask, render_template, redirect, request, jsonify
import fitz
import re
import sys
#(please ignore irrelevant imports)
app = Flask(__name__)
#app.route('/', methods=['GET'])
def index():
return render_template('index.html')
#app.route('/Analyze', methods=['GET'])
def analyze():
return render_template('analyze.html')
#(ignore this bulk one)
#app.route('/BulkAnalyze', methods=['GET'])
def bulk_analyze():
return render_template('Bulk.html')
#app.route('/api/Analyze', methods=['POST'])
def Submit():
pdf_file = request.files['FL']
jd_text = request.form['JD']
jd_text = " ".join(jd_text.split('\n'))
with fitz.open(pdf_file) as doc:
text = ''
for page in doc:
text += page.get_text()
text = ' '.join(text.split('\n'))
# Perform text comparison
matching_words = [word for word in jd_text.split() if word in text.split()]
match = 100 * len(matching_words) / len(jd_text.split())
return jsonify({'result': 'The matching percentage is :' + match })
if __name__ == "__main__":
app.run()
I was trying to compare those two text input to get a percentage output as an alert.
I am aware i could display output on page or another webpage to circumnavigate this issue, but this is not some irl problem and i would love to learn from this.
An uploaded file results in an object of type FileStorage on the server side. It is a file-like object for the loaded PDF file. It is not possible to extract the text directly from the FileStorage object. The data must first be read into a stream.
The following is the slightly modified example based on your code.
from flask import (
Flask,
jsonify,
redirect,
render_template,
request
)
import fitz
import io
import re
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.post('/api/analyze')
def api_analyze():
pcnt = 0
try:
desc = request.form['desc']
file = request.files['file']
with io.BytesIO(file.read()) as fh, fitz.open(stream=fh, filetype='pdf') as doc:
text = ' '.join(page.get_text() for page in doc)
tokens_desc = set(re.findall(r'\b\w+\b', desc))
tokens_text = set(re.findall(r'\b\w+\b', text))
tokens_match = tokens_text & tokens_desc
pcnt = len(tokens_match) / len(tokens_desc) * 100
finally:
return jsonify(result=f'The matching percentage is: {pcnt}%')
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Index</title>
</head>
<body>
<form name="upload-form">
<div>
<label for="file">Choose a file:</label>
<input type="file" name="file" id="file" accept="application/pdf" />
</div>
<div>
<label for="desc">Description:</label>
<textarea name="desc" id="desc" rows="4" cols="50"></textarea>
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(function() {
$('form[name="upload-form"]').submit(function(event) {
event.preventDefault();
const file = this['file'].files[0];
if (!file || file.type !== 'application/pdf') {
alert('Please select a valid PDF file');
return;
}
if (file.size > 5000000) {
alert('File size should not exceed 5 MB');
return;
}
$.ajax({
url: '/api/analyze',
type: 'post',
data: new FormData(this),
processData: false,
contentType: false
}).done(function(data) {
alert(data.result);
}).fail(function() {
alert('Error during API call.');
});
})
});
</script>
</body>
</html>
So I was doing a django project by following a youtube video. And I got this error:
"Forbidden (403) CSRF verification failed. Request aborted."
everytime when I submit any data in my sign-up.html file.
Then I realize I did not configure ajax correctly. So I search online and saw the django documentation of ajax. I made some changes in my html code. still, it did not solve my problem.
I am still getting the same result. What is my mistake here. Did I made any mistake while configuring ajax?
"assets/js/django-ajax.js"
This is where i save the jquery file.
Its been a while since I am stuck in this problem.
Thank you for your time
My sign-up.html file:
{% load static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Instapic</title>
<link rel="stylesheet" href="{% static 'assets/bootstrap/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'assets/css/Login-Form-Clean.css' %}">
<link rel="stylesheet" href="{% static 'assets/css/styles.css' %}">
</head>
<body>
<div class="login-clean">
<form method="post">
{% csrf_token %}
<h2 class="sr-only">Login Form</h2>
<div class="illustration">
<div style="display: none" id="errors" class="well form-error-message"></div>
<img src="{% static 'assets/img/logo.jpg' %}">
</div>
<div class="form-group">
<input class="form-control" id="username" type="text" name="username" required="" placeholder="Username" maxlength="20" minlength="4">
</div>
<div class="form-group">
<input class="form-control" id="email" type="email" name="email" required="" placeholder="Email" maxlength="100" minlength="6">
</div>
<div class="form-group">
<input class="form-control" id="password" type="password" name="password" required="" placeholder="Password" maxlength="20" minlength="6">
</div>
<div class="form-group">
<button class="btn btn-primary btn-block" id="go" type="submit">Create Account</button>
</div>Already got an account? Login here ...</form>
</div>
<script src="{% static 'assets/js/jquery.min.js' %}"></script>
<script src="{% static 'assets/bootstrap/js/bootstrap.min.js' %}"></script>
<script src="{% static 'assets/js/django-ajax.js' %}"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#go').click(function() {
$.post("ajax-sign-up",
{
username: $("#username").val(),
email: $("#email").val(),
password: $("#password").val()
},
function(data, status){
if (JSON.parse(data).Status == 'Success') {
window.location = '/';
} else {
$('#errors').html("<span>" + JSON.parse(data).Message + "</span>")
$('#errors').css('display', 'block')
}
});
return false;
})
})
</script>
</body>
</html>
My ajax configureation:
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function sameOrigin(url) {
// test that a given url is a same-origin URL
// url could be relative or scheme relative or absolute
var host = document.location.host; // host + port
var protocol = document.location.protocol;
var sr_origin = '//' + host;
var origin = protocol + sr_origin;
// Allow absolute or scheme relative URLs to same origin
return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
(url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
// or any other URL that isn't scheme relative or absolute i.e relative.
!(/^(\/\/|http:|https:).*/.test(url));
}
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
Your click handler does not prevent the event from propagating. When your event handler has run the event will propagate and will eventually submit the form as a regular browser POST. You need to prevent this from happening
$('#go').click(function(event) {
event.stopPropagation();
I would suggest, add your csrf_token in every ajax.post for test, to be sure that generated token in cookies is correct.
Example:
$.ajax({
type: 'POST',
url: "",
data: {
username: $("#username").val(),
email: $("#email").val(),
password: $("#password").val(),
'csrfmiddlewaretoken': getCookie('csrftoken')
}
})
In your code. you defined method getCookie in JS, create variable csrf_token and implement this variable in ajaxSetup. Well, token is changing, but your script does not reload it.
If you want to use ajax.setup, put inside there the method getCookie, to be sure that ajax before send retakes correct token.
There are related questions here and here but they don't solve my problem.
Let's say I have a file testdata.csv like this:
A,B
1,3
2,4
and I want the user allow to upload the file and then - just for simplicity - display its content on the same page without affecting any other elements on this page. So my desired outcome would look like this:
How would I do this? I found a couple of approaches that use a form to load the file and send its (modified) content back (to e.g. download it) but I struggle to find a solution to pass the JSON response back to modify the page (here: add the table).
This is my entire code:
from flask import Flask, render_template, request, jsonify
import pandas as pd
import numpy as np
import json
# Initialize the Flask application
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/_get_table', methods=["POST", "GET"])
def get_table():
# how to catch the file here and send its content back?
# file = ????
# print(file)
df = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
return jsonify(my_table=json.loads(df.to_json(orient="split"))["data"],
columns=[{"title": str(col)} for col in json.loads(df.to_json(orient="split"))["columns"]])
# if I use a form, I can use
# file = request.files['myfile']
# however, how do I then send the JSON response?
if __name__ == '__main__':
app.run(debug=True)
and my index.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="header">
<h3 class="text-center text-muted">Some great stuff</h3>
</div>
<hr class="mb-4">
<div class="custom-file">
<input id="myfile" name="myfile" type="file" class="custom-file-input">
<label for="myfile" class="custom-file-label">
Choose file...
</label>
</div><br><br>
<button class="btn btn-primary" type="button" id="upload_document">Upload and process!</button>
<hr class="mb-4">
<table id="pretty_table" class="table table-striped"></table>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js" type="text/javascript"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var table = null;
$('#upload_document').bind('click', function() {
$.getJSON('/_get_table', {
// what to pass here?
}, function(data) {
if (table !== null) {
table.destroy();
table = null;
$("#pretty_table").empty();
}
table = $("#pretty_table").DataTable({
data: data.my_table,
columns: data.columns
});
});
return false;
});
$('.custom-file-input').on('change', function() {
let fileName = $(this).val().split('\\').pop();
$(this).next('.custom-file-label').addClass("selected").html(fileName);
});
});
</script>
</body>
</html>
#charlietfl in the comments and this link got me on the right track. One can use FormData and then an .ajax call to achieve the desired outcome. So the important parts of above's link are (the complete files can be found below):
<form method="POST" enctype="multipart/form-data" id="fileUploadForm">
<div class="custom-file">
<input id="myfile" name="myfile" type="file" class="custom-file-input">
<label for="myfile" class="custom-file-label">
Choose file...
</label>
</div>
</form>
and
// Get form
var form = $('#fileUploadForm')[0];
// Create an FormData object
var data = new FormData(form);
and
$.ajax({
type: "POST",
enctype: 'multipart/form-data',
url: "/_get_table",
data: data,
processData: false,
The processData: false, is important to prevent jQuery form transforming the data into a query string as explained in the link above.
On the flask site, we can then easily retrieve the file by doing:
file = request.files['myfile']
df = pd.read_csv(file)
which turns the .csv into a pandas dataframe. Clearly, there should be checks etc before doing this.
The entire HTML page index_formdata.html would look like this:
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="header">
<h3 class="text-center text-muted">Some great stuff</h3>
</div>
<hr class="mb-4">
<form method="POST" enctype="multipart/form-data" id="fileUploadForm">
<div class="custom-file">
<input id="myfile" name="myfile" type="file" class="custom-file-input">
<label for="myfile" class="custom-file-label">
Choose file...
</label>
</div>
</form><br><br>
<button class="btn btn-primary" type="button" id="upload_document">Upload and process!</button>
<hr class="mb-4">
<table id="pretty_table" class="table table-striped"></table>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js" type="text/javascript"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var table = null;
$("#upload_document").click(function (event) {
//stop submit the form, we will post it manually.
event.preventDefault();
// Get form
var form = $('#fileUploadForm')[0];
// Create an FormData object
var data = new FormData(form);
// disabled the submit button
$("#upload_document").prop("disabled", true);
$.ajax({
type: "POST",
enctype: 'multipart/form-data',
url: "/_get_table",
data: data,
processData: false,
contentType: false,
cache: false,
timeout: 600000,
success: function (data) {
if (table !== null) {
table.destroy();
table = null;
$("#pretty_table").empty();
}
table = $("#pretty_table").DataTable({
data: data.my_table,
columns: data.columns
});
$("#upload_document").prop("disabled", false);
},
error: function (e) {
alert(e.responseText);
console.log("ERROR : ", e);
$("#upload_document").prop("disabled", false);
}
});
});
$('.custom-file-input').on('change', function() {
let fileName = $(this).val().split('\\').pop();
$(this).next('.custom-file-label').addClass("selected").html(fileName);
});
});
</script>
</body>
</html>
the flask file as follows:
from flask import Flask, render_template, request, jsonify
import pandas as pd
import numpy as np
import json
# Initialize the Flask application
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index_formdata.html')
#app.route('/_get_table', methods=["POST", "GET"])
def get_table():
# catch file from the form; if you have several files, just make several requests (access by name)
file = request.files['myfile']
df = pd.read_csv(file)
return jsonify(my_table=json.loads(df.to_json(orient="split"))["data"],
columns=[{"title": str(col)} for col in json.loads(df.to_json(orient="split"))["columns"]])
if __name__ == '__main__':
app.run(debug=True)
and the testdata.csv file
A,B,C
1,3,123
2,4,456
I have a HTML-page from which I pass some input values to a python script to be processed there. What I want to do is to show the results of the calculations. Just on a white page. No formatting or anything. Just plain numbers.
The problem is that I am getting just a blank page and I can't figure out why.
I did a lot of research but none of the solutions provided applied direclty to my issue.
It seems that the values are being passed on and processed since i don't get an error message nor there is an error in the error log nor any indications in the web console.
I am running a LAMP webserver which apache2 on it. The apache2 is correctly configured for running python and cgi.
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Eval Main</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<script type="text/javascript">
$(function()
{
$('#ok').click(function()
{
$.ajax(
{
url: "/cgi-bin/verlauf_Grenzen.py",
data:
{
a_test: $("#testa").val(),
v_test: $("#testv").val(),
s_test: $("#tests").val(),
j_test: $("#testj").val()
},
success: function(response)
{
handleResponse(response);
}
});
});
});
</script>
<form action="/cgi-bin/verlauf_Grenzen.py" method="POST">
<input id="testa" type="number" name="a" />
<input id="testv" type="number" name="v" />
<input id="tests" type="number" name="s" />
<input id="testj" type="number" name="j" />
<button id="ok" type="submit" value="Submit">Click</button>
</form>
</body>
</html>
verlauf_Grenzen.py
#!/usr/local/bin/python3.6
import json, time, glob, os
import numpy as np
from ruck import Cases
import cgi, cgitb
cgitb.enable()
print("Content-type:text/html\n")
def index(req):
form = cgi.FieldStorage()
a = form.getvalue('a_test')
v = form.getvalue('v_test')
s = form.getvalue('s_test')
j = form.getvalue('j_test')
cases = Cases(a,v,s,j)
#does something here
return x #x ist a single number
print("<html>")
print("<head>")
print("<title>CALC</title>")
print("</head>")
print("<body>")
print("<h2>Your name is {} </h2>".format(x))
print("</body>")
print("</html>")
req.write(x)
ruck.py
#!/usr/local/bin/python3.6
import cgi, cgitb
cgitb.enable()
class Cases(object):
def __init__(self, a, v, s, j):
self.a = a
self.v = v
self.s = s
self.j = j
# does something here (a few functions/methods)
return x, tj, ta, tv, tg, self.v, self.a
def __call__(self):
self.case_one()
self.case_two()
self.case_three()
self.case_four()
Both scripts work fine and cause no errors.
Thanks for any help in advance. I am pretty new in web programming.
There are several things wrong here, both in the JS and the Python.
Firstly, on the JS side, your event handler doesn't prevent the default submission of the form - you should use event.preventDefault(). Secondly, you don't seem to have defined the handleResponse() function that you call within the Ajax success method.
On the Python side, your verlauf_Grenzen file defines an index function but does not seem to ever call it. Inside that function, there is a return x statement halfway down which returns unconditionally, so none of the subsequent code that prints the response will ever be executed.
Finally, if you are doing anything more than trivial work, you should really consider a proper Python micro-framework such as Flask rather than using CGI.
How I solved it:
Python:
import json, time, glob, os
import numpy as np
from ruck import Cases
import cgi
form = cgi.FieldStorage()
a = form.getvalue('a_test')
v = form.getvalue('v_test')
s = form.getvalue('s_test')
j = form.getvalue('j_test')
cases = Cases(a,v,s,j)
# does something here (a few functions/methods)
json.dumps(somedata)
ajax in my HTML:
<script>
$(function(){
$('#btn2').click(function(){
$.ajax({
url: 'https://192.168.80.27/cgi-bin/verlauf_Grenzen.py',
type: 'post',
data: $.('.senddata').serialize(),
success: function(data) {
window.alert("Data sent!");
},
});
});
});
</script>
<input id="btn2" class="button" type="button" value="Send Values" value="Click"/>
<script>
$(function(){
$('#btn').click(function(){
$.ajax({
url: 'https://192.168.80.27/cgi-bin/verlauf_Grenzen.py',
type: 'json',
data: {},
success: function(data) {
var jsondata = JSON.stringify(data);
$('#xbtn').html(jsondata);
window.alert("Success!!!");
},
});
});
});
</script>
<input id="btn" class="button" type="button" value="Load results" value="Click"/>
<span id="xbtn"></span>
I have looked through previous questions involving the same error, but have not managed to find a working solution for my problem. I have a form in my html code (as part of a single-page-application) that I wish to submit to my python server through ajax.
the form in details.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>A Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">`
<link href= {{ url_for("static",filename="css/bootstrap.min.css") }} rel="stylesheet" media="screen">
</head>
<body>
<div>
<form method="post">
<label for="address">Address</label>
<input class="form-control" type="text" name="address">
<label for="postalcode">Postal Code</label>
<input class="form-control" type="text" name="postalcode"><br>
<label for="city">City</label>
<input class="form-control" type="text" name="city">
<label for="country">Country</label>
<input class="form-control" type="text" id="country_indicator" placeholder="Country" name="country">
<button id="submitForm" type="submit">Submit</button>
</form>
</div>
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
<script src={{ url_for("static", filename="js/bootstrap.min.js") }}></script>
<script src={{ url_for("static", filename="js/details.js") }}></script>
</body>
</html>
If I remove 'method="post"' from the html form, the page empties the forms and reloads, but with it I get the above mentioned error, even when the form is fully filled out. My guess is that something between the form and the JS is not working as the request.json always returns NoneType objects.
details.js:
$(document).ready(function() {
$("#submitForm").click(function(){
var address = $("#address").val();
var postalcode = $("#postalcode").val();
var city = $("#city").val();
var country = $("#country").val();
console.log(address);
var details = {
"address" : address,
"postalcode" : postalcode,
"city" : city,
"country" : country
}
console.log(details.city);
$.ajax({
type: "POST",
url: "/details",
data: JSON.stringify(details, null, '\t'),
contentType: 'application/json;charset=UTF-8',
success: function(result) {
console.log(result)
}
})
})
})
Note: I added the console.log for troubleshooting, but no text appears in the js-console, which is why I believe the problem appears already before this point.
the relevant app.route in my .py file: I am not yet using the values from details.js, I just wish to see that something is actually sent. This is why I only return "ok" for now.
#app.route('/details', methods=['GET', 'POST'])
def details():
if request.method == "POST":
print(request.json['address']) # . TypeError: 'NoneType' object has no attribute '__getitem__' - crash appears here.
return "ok")
return render_template("details.html")
So because of some problem in the previous steps, the object sent to the .py file is NoneType I assume. I am very new to python and JS, so any pointers would be greatly appreciated. Thank you in advance!
Edit: I also encountered "uncaught ReferenceError: $ is not defined" now from the javascript console, but moving the jquery- to the head solved that problem
Doh! The data isn't being sent to the server properly! I've rewritten some of your code below. I hope you don't mind, but the form will now submit with ordinary post variables instead of JSON.
#app.route('/details', methods=['GET', 'POST'])
def details():
# This is our new method, notice it's a bit streamlined.
if request.method == "POST":
# We can get the post data using request.form.get(). The first variable is the name="" attribute in HTML, and the second is the default value if it wasn't found in the data.
return "The address was %s" % request.form.get('address', 'not provided! :O')
return render_template("base.html")
Now for the Javascript!
$(document).ready(function() {
$("#submitForm").click(function(e){
// Prevent the HTML page from submitting the form since we're doing this by AJAX. This would cause duplications and other issues.
e.preventDefault();
// Get a FormData object from the page
var data = new FormData($('form')[0]);
// You don't HAVE to send your data by JSON, and in this instance it will be easier not to
$.ajax({
type: "POST",
url: "/details",
data: data,
processData: false,
contentType: false,
success: function(result) {
console.log(result)
}
})
})
})
Hope this helps! Don't forget to mark the answer as solved if this fixes it for you. :)