I'm trying to implement a simple dashboard with Flask that will:
Accept a user text input, with a "submit" button. POST this user input to flask.
Flask accepts this input, does some stuff to it, then makes a GET request to another API.
This GET request returns data and shows it somehow (can just be console.log for now)
As an example, with the star wars API:
User inputs name of a Star Wars character (assume no spelling errors)
Flask reads this input name, and maps it to an ID number, because the Star Wars API accepts id numbers. Form a GET request to the Star Wars API, to get full character information.
For now, we can just console.log character information (e.g. "height", "mass", etc.)
What I have now:
app.py
from flask import Flask, jsonify, request, render_template
import random
import json
app = Flask(__name__)
#app.route("/")
def index():
return render_template('index.html')
#app.route("/form_example", methods=["GET", "POST"])
def form_example():
if request.method == "POST":
language = request.form("character_name")
starwars_dictionary = {"Luke Skywalker":"1", "C-3PO":"2", "R2-D2": "3"}
# starwars_dictionary is a dictionary with character_name:character_number key-value pairs.
# GET URL is of the form https://swapi.co/api/people/<character_number>
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
index.html
<!DOCTYPE html>
<html>
<head>
<title>py-to-JS</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
<h3>Sample Inputs</h3>
<ul>
<li>Luke Skywalker</li>
<li>C-3PO</li>
<li>R2-D2</li>
</ul>
<form method="POST">
Enter Name: <input type="text" name="character_name"><br>
<input type="submit" value="Submit"><br>
</form>
</body>
</html>
In this current form, when I run the app, it returns "Method not allowed; this method is not allowed for the requested URL".
I'm not sure what I'm missing; it's probably just not wired together properly but I'm not sure what the proper syntax is.
Working version after implementing the accepted answer:
app.py
from flask import Flask, jsonify, request, render_template
import requests
import random
import json
app = Flask(__name__)
#app.route("/index", methods=["GET", "POST"])
def index():
#character_height = "" # init a default value of empty string...seems unwieldy
if request.method == "POST":
character_name = request.form.get("character_name")
# Map user input to a numbers
starwars_dictionary = {"Luke Skywalker":"1", "C-3PO":"2", "R2-D2": "3"}
char_id = starwars_dictionary[character_name]
url = "https://swapi.co/api/people/"+char_id
response = requests.get(url)
response_dict = json.loads(response.text)
character_height = response_dict["height"]
return render_template("index.html", character_height=character_height)
return render_template("index.html")
##app.route("/form_example", methods=["GET", "POST"])
#def form_example():
if __name__ == "__main__":
app.run(debug=True)
index.html
<!DOCTYPE html>
<html>
<head>
<title>py-to-JS</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
<h3>Sample Inputs</h3>
<ul>
<li>Luke Skywalker</li>
<li>C-3PO</li>
<li>R2-D2</li>
</ul>
<form method="POST" action="/index">
Enter Name: <input type="text" name="character_name"><br>
<input type="submit" value="Submit"><br>
</form>
{{ character_height }}
</body>
</html>
Probably the form is posting to the / endpoint, because you didn't declare a form action.
Needs to be more like:
<form method="POST" action="/form_example">
Or if you want to get snazzy and use Jinja's url_for function:
<form method="POST" action="{{ url_for('form_example') }}">
EDIT: That said, you could handle this with a single route function:
#app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
language = request.form("character_name")
starwars_dictionary = {"Luke Skywalker":"1", "C-3PO":"2", "R2-D2": "3"}
# Logic to query remote API ges here.
else: # Assume method is GET
return render_template("index.html")
Then make the form action {{ url_for('index') }}
Related
This question already has answers here:
Passing HTML to template using Flask/Jinja2
(7 answers)
Closed 4 months ago.
I have this code that compute the similarity between 2 strings:
import spacy
from spacy.lang.pt.examples import sentences
X ="some string 1"
Y ="some string 2"
nlp = spacy.load('pt_core_news_sm')
X_nlp = nlp(X)
Y_nlp = nlp(Y)
token_x = [token.text for token in X_nlp]
token_y = [token.text for token in Y_nlp]
print("Similarity:", X_nlp.similarity(Y_nlp))
Now I want to transform this code in an API with flask, I tried to follow a tutorial:
from flask import Flask,render_template,url_for,request
import re
import spacy
from spacy.lang.pt.examples import sentences
nlp = spacy.load('pt_core_news_sm')
app = Flask(__name__)
#app.route('/',methods=["POST"])
def process():
X_nlp = nlp(input())
Y_nlp = nlp(input())
print("Similarity:", X_nlp.similarity(Y_nlp))
if __name__ == '__main__':
app.run(debug=True)
the code above returns: "GET / HTTP/1.1" 405 -
You are trying to reach the URL "/". However, within your code there is no route defined for this path. Thus, the error 404 is returned.
You need a route that accepts both a GET and a POST request to both display a form and receive data from a submitted form sent via POST.
Otherwise, a 405 error is returned because the request method is not allowed.
#app.route('/', method=['GET', 'POST'])
def index():
if request.method == 'POST':
# Handle a POST request and the data sent here.
# ...
Within Flask it is not possible to request input with input().
As mentioned above, you need a form within an HTML page. Within this form you can define input fields that must be provided with a name attribute in order to query them on the server side.
<form method="POST">
<input type="text" name="x" />
<input type="text" name="y" />
<input type="submit">
</form>
If the submit button is now pressed, the data of the form, as defined in the method attribute, is sent to the server via POST and can be queried here. The input fields are queried using the name attribute.
Finally, the endpoint must have a return value. In your case, this is the template that displays the page with the form and outputs the possible result.
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
x = request.form.get('x', '')
y = reutest.form.get('y', '')
# ...
return render_template('index.html')
So the entire code of your application should look something like this.
Flask (app.py)
from flask import (
Flask,
render_template,
request
)
import spacy
nlp = spacy.load('pt_core_news_sm')
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
# Handle a POST request and the data sent here.
x_nlp = nlp(request.form.get('x', ''))
y_nlp = nlp(request.form.get('y', ''))
resultado = x_nlp.similarity(y_nlp)
# Return a rendered template and pass defined variables to the template.
return render_template('index.html', **locals())
HTML (templates/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Index</title>
</head>
<body>
<form method="POST">
<input type="text" name="x" />
<input type="text" name="y" />
<input type="submit">
</form>
{% if resultado -%}
<p>Similaridade: {{ resultado }}</p>
{% endif -%}
</body>
</html>
from flask import Flask, render_template,request
app = Flask(__name__)
#app.route('/',methods=['post'])
def main():
if (request.method=="POST"):
text=request.form('write')
if(text==None):
text=""
else:
text=""
return render_template('form.html',text=text)
if __name__ == "__main__":
app.run(debug=True)
I want to receive only POST method. So I set method option to methods=["post"]. But it always sends HTTP 405 Not Allowed Method error.
<html>
<head>
<title>Using Tag</title>
</head>
<body>
<form method="POST">
<input type="text" name="write">
<input type="submit">
</form>
{{ text }}
</body>
</html>
I want to know reason why this application only sends HTTP 405 response.
To access the HTML form from / path you need to enable both GET and POST request in that route. Otherwise when you try to access the root path / from your browser, you will get the HTTP Method not allowed error.
app.py:
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route('/', methods=['POST', 'GET'])
def main():
text = ""
if request.method == "POST":
text = request.form['username']
return render_template('form.html', text=text)
if __name__ == "__main__":
app.run(debug=True)
templates/form.html:
<html>
<head>
<title>Using Tag</title>
</head>
<body>
<form method="POST">
<input type="text" name="write">
<input type="submit">
</form>
{{ text }}
</body>
</html>
Output:
Explanation (Updated):
To access the form value use request.form['INPUT_FIELD_NAME'].
We are making GET and POST requests to the / route. So, we set GET and POST requests in the methods options of the / route. When we are viewing the page using the browser, we make GET request to that page. When we submit the form, we make POST request to that page. In this case, we are storing the form value in the text variable and pass the value to the template. For GET request we are showing the empty text value.
The above snippet is the same as the following snippet:
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route('/', methods=['POST', 'GET'])
def main():
if request.method == "POST":
text = request.form['username']
return render_template('form.html', text=text)
elif request.method == "GET":
return render_template('form.html', text="")
if __name__ == "__main__":
app.run(debug=True)
References:
Flask documentation for request object
This question already has answers here:
Get the data received in a Flask request
(23 answers)
Closed 2 years ago.
I'm new to flask and web programming in general. I'm trying a simple example. I have a HTML base template that shows a text box and a picture of an animal. The template is rendered by flask. The idea is that a user can type the name of a new animal in the text box and the picture changes to the new animal.
I tested the code. There is a problem - that the input text given in the html textbox doesn't seem to go to the proper app.route. Or at least I can't figure out (as I'm running on pythonanywhere and the print statements in the server don't show up on console).
Here is the code and the template. Please let me know what I'm doing wrong. Thanks!
Here is the flask_app.py:
from flask import render_template
from flask import request, redirect
from flask import Flask
app = Flask(__name__)
#app.route('/')
def index():
imgname = "tiger2.png"
return render_template('untitled1.html', title='TIGER', fname=imgname)
#app.route('/', methods=['POST', 'GET'])
def imgshow(animal):
#assert request.method == 'POST'
#print("New request!")
animal = request.form['animal']
if animal.lower() == 'tiger':
imgname = 'tiger2.png'
elif animal.lower() == 'lion':
imgname = 'lion1.png'
elif animal.lower() == 'panther':
imgname = 'panther.png'
else:
imgname = 'lion1.png'
return render_template('untitled1.html', title=animal.upper(), fname=imgname)
And here is the template untitled1.html
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<!-- Serving an Image -->
<h1>Hello, World!</h1>
<form action="">
<label for="animal">Animal: </label>
<input type="text" id="animal" name="animal"><br><br>
</form>
<img src="{{ url_for('static', filename=fname ) }}" alt="Tiger">
</body>
</html>
Try this:
from flask import render_template
from flask import request, redirect
from flask import Flask
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
imgname = "tiger2.png"
title='TIGER'
else:
variants = {
'tiger': 'tiger2.png',
'lion': 'lion1.png',
'panther': 'panther.png'
}
animal = request.form.get('animal').lower()
imgname = variants.get(animal)
title = animal.upper()
return render_template('untitled1.html', title='TIGER', fname=imgname)
For me, the best approach is to use only the GET method:
from flask import Flask, render_template, request, redirect
app = Flask(__name__)
animals = {'tiger': 'tiger2.png', \
'lion': 'lion1.png', \
'panther': 'panther.png'}
#app.route('/')
def index():
animal = request.args.get('animal', 'tiger')
image = animals.get(animal, 'lion')
return render_template('untitled1.html', title=animal.upper(), fname=image)
The POST method is best when you need to do some processing (write data from the database) and then redirect to another GET route.
app.route registers that under the given web address (in your case \), the function listed just below be implemented. The problem is that you have two functions registered under the same route which means that the first registration is erased.
You don't need the first app.route. It basically should say that the default value of an animal is a tiger. The second function should be modified as below:
display_map = {
'tiger': 'tiger2.png',
'lion': 'lion1.png',
'panther': 'panther.png'
}
#app.route('/', methods=['POST', 'GET'])
def imgshow(animal):
if request.method == 'POST':
animal = request.form.get('animal', 'lion').lower()
imgname = display_map.get(animal, 'lion1.png')
return render_template('untitled1.html', title=animal.upper(), fname=imgname)
else:
return render_template('untitled1.html', title='TIGER', fname='tiger.png')
And you also need to actually submit results to the server.
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<!-- Serving an Image -->
<h1>Hello, World!</h1>
<form method="POST">
<label for="animal">Animal: </label>
<input type="text" id="animal" name="animal"><br><be>
<input type="submit" name="submit" value="submit">
</form>
<img src="{{ url_for('static', filename=fname ) }}" alt="Tiger">
</body>
</html>
I'm new to using Flask and I've just been trying to pass a variable between two web pages. The first is a simple form to accept a number with the second page just displaying what is entered.
HTML for the form page:
<!doctype html>
<html>
<body>
<form action ="{{ url_for('return_form', glon="glon") }}" method="post">
Galactic Longitude: <input type="text" name="glon">
<button type="submit">Submit</button>
</form>
</body>
</html>
HTML for the display page:
<!doctype html>
<body>
<p> {{ glon }} </p>
</body>
</html>
The Flask script currently looks like this:
from flask import Flask
from flask import render_template, url_for, request, redirect
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/form/', methods = ['GET', 'POST'])
def form():
if request.method == 'POST':
glon = request.form['glon']
#glat = request.form['glat']
return redirect(url_for('return_form', glon=glon))
return render_template('form.html')
#app.route('/return_form/<glon>', methods = ['GET', 'POST'])
def return_form(glon):
return render_template('return_form.html', glon=glon)
if __name__ == '__main__':
app.run()
At the moment, the second page just displays "glon" instead of the number passed to the form.
I simply want the variable to display on the second page, and eventually use it in the return_form function.
So i didn't got your approach.Below is what i did,I changed the code a bit. Hope this solves your problem.
main.py
from flask import Flask
from flask import render_template, url_for, request, redirect
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/form', methods = ['GET', 'POST'])
def form():
if request.method == 'POST':
glon = request.form['glon']
return render_template('display.html', glon=glon)
# #app.route('/return_form/<glon>', methods = ['GET', 'POST'])
# def return_form(glon):
# return render_template('return_form.html', glon=glon)
if __name__ == '__main__':
app.run()
index.html
<html>
<body>
<form action ="{{ url_for('form') }}" method="post">
Galactic Longitude: <input type="text" name="glon">
<button type="submit">Submit</button>
</form>
</body>
</html>
display.html
<!doctype html>
<body>
<p> {{ glon }} </p>
</body>
</html>
Flask beginner here, bear with me please !
In this little piece of code I simplified for the question, I have for a defined route / with 2 forms : I'd like the add form to add things to the db and the delete form to delete things, simple.
However my issue is that in this code I can't differentiate which input button from the form is pressed as formadd.validate() and formdel.validate() both always return true.
How can I differentiate which submit button is pressed in order to manipulate the database accordingly ?
At first I wrote what is currently commented below, but obviously it doesn't work since the validate method returns true....
from flask import Flask, render_template, request
from flask.ext.sqlalchemy import SQLAlchemy
from wtforms import Form, StringField
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///botdb.db'
db = SQLAlchemy(app)
class BotFormAdd(Form):
botname = StringField('bot name')
botdescription = StringField('bot description')
class BotFormDelete(Form):
botid = StringField('bot id')
#app.route('/', methods=['GET', 'POST'])
def index():
formadd = BotFormAdd(request.form)
formdel = BotFormDelete(request.form)
if request.method == 'POST':
print(formadd.validate(), formdel.validate())
# if request.method == 'POST' and formadd.validate():
# print('in formadd')
# bot = Bot(name=formadd.botname.data, description=formadd.botdescription.data)
# db.session.add(bot)
# db.session.commit()
# return redirect(url_for('index'))
# if request.method == 'POST' and formdel.validate():
# print('in formdel')
# db.session.delete(formdel.botid.data)
# db.session.commit()
# return redirect(url_for('index'))
return render_template('index.html', title='Home', formadd=formadd, formdel=formdel)
if __name__ == '__main__':
app.run(debug=True)
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>this is a test</title>
</head>
<body>
<form method=post action="/">
<dl>
{{ formadd.botname }}
{{ formadd.botdescription }}
</dl>
<p><input type=submit name='add' value='add this'>
</form>
<form method=post action="/">
<dl>
{{ formdel.botid }}
</dl>
<p><input type=submit name='delete' value='delete this'>
</form>
</body>
</html>
There's lots of ways of doing this, but it breaks down into two categories-- either you indicate it via the route, or via an element on a form.
Most people would just add separate routes:
#app.route('/delete-bot/', methods=['post'])
def delete_bot():
form = BotFormDelete()
if form.validate():
delete_bot(id=form.botid.data)
flash('Bot is GONE')
return redirect(url_for('index'))
So your delete form would submit to that route, get processed and sent back to index.
<form method='post' action='url_for('delete_bot')>
And you'd have a different route for adding a bot.
Alternatively you could check what type of form it was from it's contents. E.g.
if request.form.get('botid'):
# it has a botid field, it must be a deletion request
form = BotFormDelete()
form.validate()
delete_bot(form.botid.data)
else:
form = BotFormAdd()
....
But that way seems like it would get messy quickly.