Sending Data from html to Flask - python

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').)

Related

POST method 405 and 404 errors in bottle

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.

Flask form validation - CSRF token middleware

I have an html form, and I would like to insure that all submissions come from my website. I think I have seen people using a key for this (I believe this happens in Django?), and might have some ideas on how to go with that. Is there any standard way to do this in Flask?
Edit:
Now I know I'm talking about CSRF token middleware. Again, is there any standard way of doing this in Flask? How can I store the key on the server side?
In flask you can do CSRF protection using Flask-SeaSurf.There are other methods also but it is straight forward.
To start Just do pip install flask-seasurf and you are ready
import Flask
from flask_seasurf import SeaSurf
app = Flask(__name__)
csrf = SeaSurf(app)
<form method="POST">
...
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
</form>
#csrf.exempt
#app.route('/exempt_view', methods=['POST'])
def exempt_view():
'''This view is exempted from CSRF validation.'''
return 'foobar'
For more information you can visit official website
Please mark this as answer if this solves you problem.

How can I use and call a python function within HTML while using Flask?

I am new to Flask and even though I have read through the documentation, I am still very confused on the relationship between Python functions and HTML. Specifically, I am unsure of how a function can be called within an HTML page. For example, I have the following code on my route.py file:
from flask import Flask, render_template
import requests
app = Flask(__name__)
#app.route('/placement_debugger')
def placementDebugger():
return render_template('placement_debugger.html')
def get_data():
return requests.get('http://example.com'+placementID).content
Here is the code from my "placement_debugger.html" file. Basically, I am trying to obtain an ID from a user and use that ID within an HTTP GET request:
<p1>
<form action="/action_page.php">
<strong>Placement ID: </strong><input type="text" name="Placement ID"
value=""><br>
<input type="submit" value="Submit">
</form>
</p1>
How can I call my "get_data()" function within the "placement_debugger.html" page?
You can use request to get the data from your HTML inputs.
First import it from flask
from flask import request
By default Flask only allows GET requests. You'll need to allow POST methods to your route.
#app.route('/placement_debugger', methods=['GET', 'POST')
Here is how you can get the data from the HMTL form. (This would go in your get_data() function)
if request.form:
placement_id = request.form.get('Placement ID')

Why and how http method of current endpoint affects url_for behavior in Flask?

I am using:
Flask 0.12.2
Python 3.6.1
Consider this, isolated, example of the behavior I am pondering on:
from flask import Flask, url_for, render_template_string
app = Flask(__name__)
#app.route('/hi/', methods=['POST'])
#app.route('/hi/<var>')
def hi(var):
return ''
#app.route('/')
def index():
return render_template_string('''
<html>
<head>
<title>GET or POST</title>
</head>
<body>
<form action="{{ url_for('path') }}">
<input type='SUBMIT' value='GET'>
</form>
<form action="{{ url_for('path') }}" method='POST'>
<input type='SUBMIT' value='POST'>
</form>
</body>
</html>''')
#app.route('/path/', methods=['GET', 'POST'])
def path():
return str(url_for('hi', var='Hello', var2='xyz'))
To make my intentions clear, I will briefly describe what is happening and what I am striving to understand:
/hi/ endpoint has an 'optional' parameter (<var>), which is accepted only via GET request. 'Plain' (i.e. without arguments) /hi/ endpoint can only be accessed via POST method.
/path/ endpoint can be accessed via both GET and POST http methods. And it just returns path for hi generated via url_for('hi', var='Hello', var2='xyz')
Now, I would expect /path/ to return the same string, regardless of which method was used to access it (GET or POST). But it is not the case: for GET it returns /hi/Hello?var2=xyz (as I, actually, would expect), but for POST I am getting /hi/?var=Hello&var2=xyz (which strikes me as odd behavior).
Through trials and errors I was able to find out that adding POST to methods allowed for /hi/<var> fixes the issue (/hi/Hello?var2=xyz is returned by /path/ for both GET and POST), i.e.:
#app.route('/hi/', methods=['POST'])
#app.route('/hi/<var>', methods=['GET', 'POST'])
def hi(var):
...
I hope, someone would be able to explain the following, for me:
Why is this (/path/ returns different values for POST and GET) happening?
Is it possible to avoid this behavior, without allowing POST on /hi/<var>?
I have stumbled upon answers thanks to another question =)
Addressing my own questions:
(not 100% sure about that, would be grateful if someone confirmed that I am correct here) url_for has an optional _method parameter, which is defaulted to the method that was used to return current view. So, /path/ is really returning return str(url_for('hi', var='Hello', var2='xyz', _method='GET') when it was accessed via GET request and return str(url_for('hi', var='Hello', var2='xyz', _method='POST') if it was accessed via POST request. That is why allowing POST on both endpoints (/hi/<var> and /hi/) fixes the problem — if POST is allowed only for /hi/, then return str(url_for('hi', var='Hello', var2='xyz', _method='POST') is checking whether var is known only to /hi/ (and it is, obviously, is not known to it). On the other hand, if POST is allowed on both endpoints, then /hi/ and /hi/<var> are checked for the presence of var and /hi/<var> is correctly selected.
Given the previous point, the fix is pretty obvious now: return str(url_for('hi', var='Hello', var2='xyz', _method='GET') should be substituted for return str(url_for('hi', var='Hello', var2='xyz'), in the original snippet.

Sending HTML form input to python script

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.

Categories