This is my js function for ajax form submit:
$('.sendButton').on('click', function(){
$.post( "/send-form", $('#contactForm').serialize())
}
This is my form:
<form id="contactForm">
<input id="fastName" name="name" type="text" />
<input id="fastPhone" name="phone" type="text" />
<div class="sendButton">send</div>
</form>
And this is my backend function to handle it, I am using python, flask
#app.route("/send-form", methods=['POST'])
def send_form():
data = request.data
print request.data
And in my console it prints data:
127.0.0.1 - - [22/Feb/2015 23:50:54] "POST /send-form HTTP/1.1" 200 - name=dfdsf&phone=89920203725
But I did the same in another project and it prints nothing.
Details are here:
Can't get post ajax request data python
How can this be possible?
The data is in the field request.form, and can be fetched by:
name = request.form['name']
phone = request.form['phone']
Remark. If they are not sent with, this code will throw an exception.
Longer explanation
The reason is that the data is sent using a encoding known as application/x-www-form-urlencoded. The data is in request.data field, but to decode it you can use request.form, and Flask will automatically decode it as application/x-www-form-urlencoded.
Related
I am building a dictionary app with Flask where users can add new words, I am trying to request the word from the word input , I am having issues with the POST request, the error I am receiving on my terminal is this:
line 50, in add_word
word = req['word']
keyError:'word'
and this is how I wrote the code in my app.py file:
#app.route('/word', methods= ['POST'])
def add_word():
req = request.get_json()
word = req['word']
meaning = req['meaning']
conn = mysql.get_db()
cur = conn.cursor()
cur.execute('insert into word(word, meaning) VALUES (%s, %s)',(word, meaning))
conn.commit()
cur.close()
return json.dumps("success")
here is the json in my JavaScript file, I am posting to my flask app:
$('#word-form').submit(function() {
let word = $('word').val();
let meaning = $('meaning').val();
$.ajax({
url: '/word',
type: 'POST',
dataType: 'json',
data : JSON.stringify({
'word': word,
'meaning': meaning
}),
contentType: 'application/json, charset = UTF-8',
success: function(data) {
location.reload();
},
error: function(err) {
console.log(err);
}
})
here is the Html page:
<div class="div col-md-2 sidenav">
All words
Add New
<div>
<form action="javascript:0" id="word-form">
<div class="form-group">
<label for="word">Word:</label>
<input type="text"
class="form-control"
name="word"
id="word"
placeholder="Type in the word here:"
required>
</div>
<div class="form-group">
<label for="Meaning">Meaning:</label>
<textarea class="form-control" id="meaning"
placeholder="enter the meaning here: " required></textarea>
</div>
<button type="submit" class="btn btn-primary btn-block btn-lg" id="submit">Submit</button>
<button type="button" class="btn btn-warning btn-block btn-lg" id="cancel">Cancel</button>
</form>
</div>
</div>
<div class="div col-md-10 main">
<table style="border: 2px;">
<thead>
<tr>
<th>SN</th>
<th>Word</th>
<th>Meaning</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{% for word in words %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ word['word'] }}</td>
<td>{{ word['meaning'] }}</td>
<td><button class="btn btn-sm btn-success btn-block edit" id="{{word['id']}}">Edit</button></td>
<td><button class="btn btn-sm btn-danger btn-block delete" id="{{word['id']}}">Delete</button></td>
</tr>
{% else %}
<tr>
<td colspan="3">The dictionary has no words at the moment, please come bay later</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
Things seem to be in a confused state in the client code, and potentially the application architecture in general.
There are two general approaches to designing web apps that impacts how you create routes and build requests. One approach is the AJAX-based single page app architecture that loads one HTML skeleton, then uses JS (jQuery here) to make AJAX requests to a JSON API and injects the response data into the page using JS instead of page refreshes and navigations. Since you don't have a client-side router, this doesn't qualify as a SPA, but it's worth understanding to provide context on your design.
On the other hand, you can use HTML form submissions (<form action="/path/to/resource" method="POST">) and render_template to display new pages with a browser refresh for all form submissions.
The code here is somewhere in the middle, which is potentially fine (you can use AJAX for certain events and form submissions but mostly rely on full-navigation templates for routes). But it's important to be clear on the request-response workflow you're adopting so the design makes sense and can be debugged.
Here are few oddities/inconsistencies in your current design:
return json.dumps("success") is not really JSON as it seems like you want it to be--use jsonify and a dictionary or list, e.g. jsonify({"status": "success"}; it's customary for JSON routes to return JSON responses if they aren't rendering templates or redirecting.
The client ignores the JSON response and calls location.reload. If you're just planning on reloading and you have no special client processing to do, there's not much point in using AJAX or JSON here--just submit the form to the backend and redirect to the template or static HTML page you want to show next. No client-side JS involved. Redirect to an error page or render a template with errors shown on the form on error.
Links with href="#" are poor practice. Better to use buttons if you're adding JS to these handlers and you don't want them to trigger navigation. This is semantically more appropriate and doesn't hijack the URL.
<form action="javascript:0" id="word-form"> looks like it's trying to prevent the form submission, but all this does is replace the page content with the character "0". I can't imagine how this is useful or desirable. Submitting a form to a JSON route can produce the error you're seeing--another sign of confusion about which architecture you're following. Use event.preventDefault() (add the event parameter to the callback to .submit()) to prevent the form submission from refreshing the page.
After you've prevented the page refresh, you can debug the AJAX request.
When a route is complaining about missing keys, consider that objects with keys pointing to undefined disappear when serialized as JSON (undefined is not a thing in JSON):
const word = undefined;
const foo = 42;
const bar = "baz";
console.log({word, foo, bar}); /* =>
{
"word": undefined,
"foo": 42,
"bar": "baz"
}
*/
console.log(JSON.stringify({
word,
foo,
bar,
})); // => {"foo":42,"bar":"baz"}
If you add a console.log to see if your values are there (or print the JSON on the backend route before indexing into it), these values aren't defined:
let word = $('word').val();
let meaning = $('meaning').val();
console.log(word, meaning); // <-- undefined, undefined
Why? The reason is that these selectors are missing the # symbol prefix to denote an id. Without it, jQuery looks for <word></word> and <meaning></meaning> HTML elements that don't exist.
Change these lines to:
const word = $('#word').val();
const meaning = $('#meaning').val();
and now your request body should be ready to send.
Next problem: $.ajax's dataType key specifies the response type, not the request type. Use dataType: "json" to specify the appropriate request header to trigger the Flask handler to JSON parse the request body.
After these changes, things should work, with the caveat that it might be time for a rethink of your overall design, or at least a redesign of this route workflow.
A word of advice: work slowly and test all of your assuptions at each step. The code here shows many errors that are hard to debug because they're stacked on top of each other. Isolate and validate each behavior in your app. For example, when adding the jQuery submit handler and collecting the form values, print them to make sure they're actually there as you expected.
In case you're stuck, here's minimal, complete, runnable code you can reference.
app.py:
from flask import (
Flask, jsonify, render_template, request, url_for
)
app = Flask(__name__)
#app.route("/")
def index():
return render_template("index.html")
#app.post("/words/")
def words():
payload = request.get_json()
word = payload.get("word")
meaning = payload.get("meaning")
if word is None or meaning is None:
return (jsonify({
"error": "missing required keys `word` or `meaning`"
}), 400)
# handle db operation and report failure as above
return jsonify({"status": "success"})
if __name__ == "__main__":
app.run(debug=True)
templates/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<form id="word-form">
<div>
<label for="word">Word:</label>
<input
name="word"
id="word"
placeholder="Type in the word here:"
required
>
</div>
<div>
<label for="meaning">Meaning:</label>
<textarea
name="meaning"
id="meaning"
placeholder="enter the meaning here: "
required
></textarea>
</div>
<button type="submit">Submit</button>
</form>
<script>
$('#word-form').submit(function (e) {
e.preventDefault();
const word = $('#word').val();
const meaning = $('#meaning').val();
console.log(word, meaning);
$.ajax({
url: "{{url_for('words')}}",
type: "POST",
dataType: "json",
contentType: "application/json",
data: JSON.stringify({word, meaning}),
success: function (data) {
console.log(data);
},
error: function (err) {
console.error(err);
}
});
});
</script>
</body>
</html>
See also: How to get POSTed JSON in Flask?
Here are a few additional notes that are somewhat tangential to the main issue but have to be mentioned.
You have <label for="Meaning"> but no name="meaning" element to shift focus to when clicked.
It's another antipattern to put ids on everything promiscuously. Only add ids to elements when they must have one because you're using it for something specific. Prefer classes, especially for styling.
On the backend, the code here is unsafe:
req = request.get_json()
word = req['word']
meaning = req['meaning']
If your client gives a bad request with missing values, you should detect that and return a 400/422 response (or similar) rather than crashing.
For example (from the above code snippet):
req = request.get_json()
word = req.get("word")
meaning = req.get("meaning")
if word is None or meaning is None:
return (jsonify({
"error": "missing required keys `word` or `meaning`"
}), 400)
Similarly, don't assume the database operation will succeed. Always check for errors and return an appropriate response to the client.
Resources are usually plural, not singular: words, users, posts.
I have a python script that gets 5 inputs, does some work, and then prints output for 10 seconds until it finishes. I would like that input to be taken from a web app, sent to the server, executed in python and then the result, sent back to the web app to be shown to the client.
My question is this done? I was thinking in having a HTML+CSS+VUE front end, but I don't know how to link the inputs from the html, to the python script and then back to the html async ( because I want the page to show the real time results ). What backend should I use and how to link those inputs to and results to the web page?
Thank you!
You can use a basic API.
You will create a form like this:
<form method="POST action="/your/path">
<input type="text" name="input1">
<input type="text" name="input2">
<input type="text" name="input3">
<input type="text" name="input4">
<input type="text" name="input5">
<button type="submit" value="Submit">
</form>
I don't know which framework you use but I suppose that you are using Flask:
#app.route("/your/path", methods=["POST"])
def executeTheInfos():
#Execute your code and render your HTML page to return
return yourRenderedPage
When you submit the data to HTML form, it will POST the info to /your/path in JSON format like {"input1":"value1", "input2":"value2"...} and at the back-end you will render the response page and return it to user.
After POST request, there is confirmation(jquery) box appears in browser.
So How do I select confirmation box using request api ?
Here HTML page :
<div class="sent_sms_bg">
<form name="smsAction" id="smsAction" action="/sms_all_action" method="post">
<p><input name="" id="select_all" type="checkbox" value="" /> Select All</p>
<p><input name="Delete_SMS" type="button" class="delete_selected" value="Delete Selected" />
</p>
</div>
and What I'm trying :
self.session = requests.session()
..
...
url = 'http://www.indyarocks.com/sms_all_action'
form = {
'select_all' : 'on',
'Delete_SMS': 'Delete Selected',
}
status = self.session.post(url , data = form)
POSTing directly to the url should work, as presumably the website has a form with an ajax popup to make sure you really want to submit the form, THEN POSTs the data to the URL. It wouldn't work to POST the data first and then fire the popup (as the form is already submitted so it's too late to do anything about it)
File name mypage.py
Python code
form = cgi.FieldStorage()
name = form.getfirst('name', 'empty')
id = form.getfirst('id', 'empty')
print """\
Content-Type: text/html\n
<html>
<body>
<p>Name: "%s"</p>
<p>ID: "%s"</p>
</body>
</html>
""" % (name, id)
HTML inside the same file
<form name="frm" method="post" action="mypage.py?id=33">
<input type="text" name="name" value="MyName" />
<input type="Submit" id="btn" value="Send" />
After submitting the form (pressing Send button), I can see this URL with following output
localhost:8000/cgi-bin/mypage.py?id=33
Name: "empty"
ID: "33"
if I change the form method POST to GET
<form name="frm" method="get" action="mypage.py?id=33">
then I can see this URL with following output
localhost:8000/cgi-bin/mypage.py?name=MyName
Name: "MyName"
ID: "empty"
I don't understand why I am not receiving text field value with POST method ? And why I am unable to receive id value in query string with GET method ?
Its simple python page without any framework. BTW I am using "python-bugzilla 0.8.0" downloaded from here but i think my given code is just a simple page and has nothing to do with this package.
Any help would be appreciated.
Thanks in advance,
Your GET is proper:
<form name="frm" method="get" action="mypage.py?id=33">
But your POST is not:
<form name="frm" method="post" action="mypage.py?id=33">
You can't add a GET style variable (?id=33) to the action of your POST. It should be:
<form name="frm" method="post" action="mypage.py">
<input type="hidden" name="id" value="33">
See HTTP Methods: GET vs. POST: "Query strings (name/value pairs) are sent in the URL of a GET request" and "Query strings (name/value pairs) are sent in the HTTP message body of a POST request".
Not adhering to these rules would cause unexpected results such as you are seeing.
Technically, a POST target url should not have GET parameters, so the ?id=33 in the target is invalid. I'm also guessing that it's confusing to the FieldStorage module, that might be why you are getting unexpected results.
You should properly use POST and GET per my other answer. That said, I'm worried about your use of form.getfirst and variable names.
Per the documentation:
FieldStorage.getfirst(name[, default]) - This method always returns only one value associated with form field name. The method returns only the first value in case that more values were posted under such name.
You've named your name variable name which is a silly name. See, lots of names. And your form has a name. And it's a field. Same with ID. You should change your variable names as such:
<form name="MyForm" method="post" action="mypage.py">
<input type="text" name="FullName" value="MyName" />
<input type="text" name="FormID" value="33" />
<input type="Submit" id="btn" value="Send" />
and change your Python as follows:
form = cgi.FieldStorage()
FullName = form.getfirst('FullName', 'empty')
FormID = form.getfirst('FormID', 'empty')
print """\
Content-Type: text/html\n
<html>
<body>
<p>Name: "%s"</p>
<p>ID: "%s"</p>
</body>
</html>
""" % (FullName, FormID)
That's the code for a proper POST and printing of the variables. Does it work?
Thanks for the help.
problem was here.
form = cgi.FieldStorage()
As I've mentioned in comments of your answers that I've printed "form" and here is output: "FieldStorage(None, None, [])".
So, if FieldStorage doesn't have any value then it doesn't matter which function is being used to get the form value. But it was really good information and practical as well.
previously form = cgi.FieldStorage() was declared inside another function which was wrong, that's why FieldStorage was empty.
Here is WRONG code.
def myfunction ():
cgitb.enable()
form = cgi.FieldStorage()
Solution 1:
form = cgi.FieldStorage() shall define inside the run() function and pass this form as parameter of other function to get values of form.
i.e.
def run():
cgitb.enable()
form = cgi.FieldStorage()
myfunction(form)
Now its working
def myfunction (form):
name = form.getfirst('name', 'empty')
id = form.getfirst('id', 'empty')
Solution 2:
form = cgi.FieldStorage() shall define directly inside the main function then don't need to pass it as parameter .
i.e.
if __name__ == "__main__":
cgitb.enable()
form = cgi.FieldStorage()
Now its working too and form is accessible inside the myfunction.
def myfunction ():
name = form.getfirst('name', 'empty')
id = form.getfirst('id', 'empty')
Thanks everybody.
my question is what is really happens when you hit the submit button on a html servey like this one?
<INPUT TYPE="radio" NAME="bev" VALUE="no" CHECKED>No beverage<BR>
<INPUT TYPE="radio" NAME="bev" VALUE="tea">Tea<BR>
<INPUT TYPE="radio" NAME="bev" VALUE="cof">Coffee<BR>
<INPUT TYPE="radio" NAME="bev" VALUE="lem">Lemonade<BR>
To be more specific, I mean how does the browser sending the data of my choie to the server, because I want to make a Python code that will vote for me in a HTML survey like this
If the form method attribute is post(which I think is) , then the browser sends a post request.If you're using requests library, this is the code
data = {'bev': 'tea'}
#Define a dict with parameter with keys as name attribute values and value as the content you want to send
r = requests.get("http://awebsite.com/", params=data)
print r.content
Requests docs POST requests
And if you aren't using requests, then God help you write the code.