I am very very new to Python and Bottle and how server requests/responses work, and there are still quite a few basic and general ideas that I am trying to wrap my head around. That being said, there's some fundamental logic that I am missing out on.
In order to familiarize myself with bottle, python, html, etc. I have this very basic project. I have a form written in html (stored in tpl file) with some radios, checkboxes, select menus, and text input fields. I want to store all of those inputs and print them out to a new results page when the user clicks submit. As a sort of base trial to store all of the variables, I started with just the first name variable, and I wanted to print the stored variable to the url ('/fname'). If I can get this working then my plan is to change the post method to route to ('/fanpage/results') and then return all of the information on that page (possibly using another template, but I haven't gotten that far yet)
from bottle import route, run, post, get, request, template, static_file
HOST = 'localhost'
(...)
#post('/fname')
def show_fname():
fname = request.forms.get('fname')
return "<p>Your name is {{fname}}.</p>"
(...)
run(host=HOST, port=8080, debug=True)
(...)
<form action="/fanpage/results" class="needs-validated">
(...)
<div class="row">
<div class="col>
<label for="fname" class="form-label">First name:</label>
<input type="first name" class="form-control" id="fname" name="fname" placeholder="Enter first name" title="Please enter your first name" required>
<div class="invalid-feedback">Please fill out this field to continue.</div>
</div>
</div>
(...)
</form>
I have some route and get methods before the post method to send the tpl files to the server to display the form, which work. When I try to go to localhost:8080/fname, though, I get a 405 error - method not allowed. I feel like I am directly copying what I've seen in the documentation and online and I'm not sure why the url isn't working. If I try to change the route for the post method to the url ('/fanpage/results'), I get a 404 not found error.
Also, a basic question: If I'm working with multiple template files and trying to pull information, such as the first name, from just one of the templates, how does it know which template to pull data from? Could that be the reason for the 405 error?
Also, a basic question: If I'm working with multiple template files and trying to pull information, such as the first name, from just one of the templates, how does it know which template to pull data from? Could that be the reason for the 405 error?
No data is "pulled" from templates as such. The workflow is like this:
Client sends a request to a server's route. The request is typically a GET or POST request method, and the route is something like localhost:8080/fname. The server has to expose a matching request method and route.
The server takes action on the data sent with the request (if any) and sends a response. The response might be a simple status code (like a 405 Method Not Allowed) or it could have a body data payload along with it. The body could be an HTML page, CSS, JSON or something else. To build an HTML page, the server will want to inject dynamic data into a template. Other times, the HTML is purely static (unchanging).
When the response is received from the server, the client (which could be a web browser or a utility like curl or postman) shows the response to the user in whatever form it's designed to (web browsers render the HTML, for example, which often involves kicking off more requests to get relevant CSS files, scripts or images included in the HTML).
Now, let's say the response was HTML, possibly the result of rendering a template on the server. If that HTML has a form in it (or JS code that triggers a request asynchronously, avoiding a page refresh), the user can re-submit the form and kick off the process all over again.
One more thing: when you navigate your browser to, say https://en.wikipedia.org or localhost:8080/fname, that's a GET request, not a POST request, which explains the 405 response code since you told /fname to only respond to POST requests.
If you want to test a POST route, either use curl, postman or write a simple GET route that responds with a form that looks like <form action="/fname" method="post">.
Here's a minimal example of all of this wired together:
server.py
from bottle import post, request, route, run, template
#route("/")
def index():
return template("index.tpl")
#post("/fname")
def fname():
return template("fname.tpl", fname=request.forms.get("fname"))
if __name__ == "__main__":
run(host="localhost", port=8080, debug=True, reloader=True)
index.tpl
<!DOCTYPE html>
<html>
<body>
<form action="/fname" method="post">
<label for="fname">First name:</label>
<input name="fname" placeholder="Enter first name" required>
<input type="submit">
</form>
</body>
</html>
fname.tpl
<!DOCTYPE html>
<html>
<body>
<p>Your name is {{fname}}.</p>
<p>Back</p>
</body>
</html>
You can start a server, then navigate to http://localhost:8080 to fill out the form and see the result, or use a utility like curl on the command line to POST the fname:
PS C:\Users\me> curl -X POST -F 'fname=foo' http://localhost:8080/fname
<!DOCTYPE html>
<html>
<body>
<p>Your name is foo.</p>
<p>Back</p>
</body>
</html>
You can see the server responded with HTML, the result of injecting the fname variable from the POST payload into the fname.tpl template. A browser would render this as a webpage, but on the command line you just see the HTML code. In a JSON API, the response on the command line would be easier to manipulate, but here the server is designed for clients using browsers.
Related
I have implemented a form using iron-form and I am able to process it in the server, but then I can't figure out how to get the response out, I cannot redirect to another page or refresh. If I remove the 'is="iron-form' then it works fine. I'm using polymer 1.0, google app engine, and jinja2 for templating.
HTML:
<div class="c-form">
<h1>"Contact us"</h1>
<p> {{ sucess-message }} </p>
<form is="iron-form" id="contact-form" method="post" action="/formhandler">
<paper-input label="Name" name="name" required></paper-input>
<paper-textarea label="Message" name="msj" required></paper-textarea>
<paper-button raised class="colorful" onclick="submitForm()">Send</paper-button>
</form>
</div>
<script>
function submitForm() {
document.getElementById('contact-form').submit();
}
</script>
main.py:
class FormHandler(MainHandler):
def post(self):
self.name = self.request.get('name')
self.msj = self.request.get('msj')
mesj = MensajesDB.new_msj(self.name, self.msj)
mesj.put()
self.respose.out.write("SUCCESS") //Here I would like to render the template,
but I can't get it to render or redirect to
another page...
class MainPage(MainHandler):
def get(self):
self.render("home.html", route="home", title="Home")
app = webapp2.WSGIApplication([
('/', MainPage),
('/formhandler', FormHandler),
], debug=True)
It seems like you might be having trouble debugging where the failure is happening. I suggest familiarizing yourself with the Chrome DevTools -- specifically, the Network tab, which will allow you to monitor the requests from your JS to your server, and then the server responses.
From your server-side application, you can access the entire request body from within your FormHandler.post method using the self.request.POST variable, as documented here.
If you want to redirect after saving to the Datastore, you'll need to remove the self.response.out.write() line and use self.redirect() instead. Note that you cannot send a response body/string and redirect in a single response.
So I'm trying to send data from an html form to my python flask framework.
Here's the example of the html code I'm using
<form method=post action=/test>
<input name=Name value=Austin type=hidden><input type=submit value="Add Notification">
and here's the python flask I'm working with
#app.route('/test', methods=('GET', 'POST')
def test_page():
v = request.values.get('Name')
return v
I've tried many different request methods and can't seem to get it to work and I get a 405 error. I'm not very familiar with the flask web development or using post requests. If anyone could point me in the correct direction then that'd be great!
You're POSTing to your endpoint, but app.route by default only enables GET. Change app.route('/test') to app.route('/test', methods=('GET', 'POST')), and you'll be able to access your endpoint.
That 405 response you're getting is Method Not Allowed.
(Unrelated issue, request.values.get['Name'] should be request.values.get('Name').)
After reading many similar sounding problems and the relevant Flask docs, I cannot seem to figure out what is generating the following error upon submitting a form:
400 Bad Request
The browser (or proxy) sent a request that this server could not understand.
While the form always displays properly, the bad request happens when I submit an HTML form that ties to either of these functions:
#app.route('/app/business', methods=['GET', 'POST'])
def apply_business():
if request.method == 'POST':
new_account = Business(name=request.form['name_field'], email=request.form['email_field'], account_type="business",
q1=request.form['q1_field'], q2=request.form['q2_field'], q3=request.form['q3_field'], q4=request.form['q4_field'],
q5=request.form['q5_field'], q6=request.form['q6_field'], q7=request.form['q7_field'],
account_status="pending", time=datetime.datetime.utcnow())
db.session.add(new_account)
db.session.commit()
session['name'] = request.form['name_field']
return redirect(url_for('success'))
return render_template('application.html', accounttype="business")
#app.route('/app/student', methods=['GET', 'POST'])
def apply_student():
if request.method == 'POST':
new_account = Student(name=request.form['name_field'], email=request.form['email_field'], account_type="student",
q1=request.form['q1_field'], q2=request.form['q2_field'], q3=request.form['q3_field'], q4=request.form['q4_field'],
q5=request.form['q5_field'], q6=request.form['q6_field'], q7=request.form['q7_field'], q8=request.form['q8_field'],
q9=request.form['q9_field'], q10=request.form['q10_field'],
account_status="pending", time=datetime.datetime.utcnow())
db.session.add(new_account)
db.session.commit()
session['name'] = request.form['name_field']
return redirect(url_for('success'))
return render_template('application.html', accounttype="student")
The relevant part of HTML is
<html>
<head>
<title>apply</title>
</head>
<body>
{% if accounttype=="business" %}
<form action="{{ url_for('apply_business') }}" method=post class="application_form">
{% elif accounttype=="student" %}
<form action="{{ url_for('apply_student') }}" method=post class="application_form">
{% endif %}
<p>Full Name:</p>
<input name="name_field" placeholder="First and Last">
<p>Email Address:</p>
<input name="email_field" placeholder="your#email.com">
...
The problem for most people was not calling GET or POST, but I am doing just that in both functions, and I double checked to make sure I imported everything necessary, such as from flask import request. I also queried the database and confirmed that the additions from the form weren't added.
In the Flask app, I was requesting form fields that were labeled slightly different in the HTML form. Keeping the names consistent is a must. More can be read at this question Form sending error, Flask
The solution was simple and uncovered in the comments. As addressed in this question, Form sending error, Flask, and pointed out by Sean Vieira,
...the issue is that Flask raises an HTTP error when it fails to find a
key in the args and form dictionaries. What Flask assumes by default
is that if you are asking for a particular key and it's not there then
something got left out of the request and the entire request is
invalid.
In other words, if only one form element that you request in Python cannot be found in HTML, then the POST request is not valid and the error appears, in my case without any irregularities in the traceback. For me, it was a lack of consistency with spelling: in the HTML, I labeled various form inputs
<input name="question1_field" placeholder="question one">
while in Python, when there was a POST called, I grab a nonexistent form with
request.form['question1']
whereas, to be consistent with my HTML form names, it needed to be
request.form['question1_field']
I'm trying to send a HTML form input from a page to a python script. This is just for testing purposes so ignore the poor code. Basically I don't know how to define the action= part to reference the search() function in the python script. I just need to get the basic functionality working. I'm using web2py as the framework which is probably relevant:
Controller: default.py
def index():
return dict()
def search():
import urllib
q=request.vars.q
result1 = urllib.urlopen('http://www.google.com/search?q=%s' % q)
return dict(result1=result1)
default/search.html:
{{extend 'layout.html'}}
<form method=get action=???>
<input name=q>
<input type=submit value=search>
</form>
It looks like the form itself is served via the search() function, so you can just set action="", and it will submit back to the search() function. Also, you should put quotes around your HTML attribute values, and you should add some logic to check whether there is anything in request.vars.q, because when the page first loads with an empty form, there is no query to process.
What you are doing there is correct for GET requests. For POST requests you need to pass the fields as the data parameter.
urllib.urlopen('http://www.google.com/search', data=urllib.urlencode({'q':'FOO'}))
does the search as a POST request for example.
See here for more info.
I have an HTML file that has a web page design with a single form, for a user to enter his name. I want to create an six entry array for every submission (to later be filled with information on another page)
Is Django the proper utility to use for this? I would like to have the html design file and the python back end processing as separate files. If so, can anyone point me towards a good place to read about integrating HTML and underlying python codes that process HTML submission forms?
Django may be overkill for this. If all you want is a way to link a form to some backend Python code, a micro framework like Flask might be a better choice.
Here is how you do a simple form with Flask:
Create a directory project and inside it, a directory templates
Your template is simple:
{% if name %}
Hello {{ name }}
{% endif %}
<form method="POST">
<input type="text" name="name" value="Enter your name">
<input type="submit">
</form>
Save that as index.html in the templates subdirectory.
Create a file called go.py in the project directory, and in it copy and paste this:
from flask import Flask
from flask import render_template
from flask import request
app = Flask(__name__)
#app.route('/',methods=['POST','GET'])
def process_form():
if request.method == 'POST':
form_input = request.form['name']
return render_template('index.html',name=form_input)
else:
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
Finally from the project directory, type:
python go.py
Open your browser and go to http://localhost:5000/
You can create html form in Django, though you may have to convert the form to a template.
In case this you first time to use django, you may go though the official Django book
Django provides a template system (in which the presentation files are separate from the business logic and the entire system is highly decoupled). The template language is simple (but very powerful) text substitution on top of an existing text file. You can use the Django templates in HTML (full or partial), text, XML, JSON, or nearly any other text-based format.
Django also allows you to hook into another template system, but I don't know a lot about how that works.
Look at the Django template documentation for more information.