I am struggling with text fields in Flask. What I want to achieve is line wrapping in the HTML forms. I tried different suggestions that I found in related HTML-threads but without luck.
E.g., let's assume I have a simple flask app with a text field, and I want to wrap the text instead of extending to the right:
My flask app looks like this:
from flask import Flask, render_template, request
from wtforms import Form, TextField, validators
app = Flask(__name__)
class ReviewForm(Form):
moviereview = TextField('My Review: ', [validators.DataRequired()])
#app.route('/')
def index():
form = ReviewForm(request.form, csrf_enabled=False)
return render_template('reviewform.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
And the template that it should render is written like this:
./templates/reviewform.html
<!doctype html>
<html>
<head>
</head>
<body>
<form method=post action="/results">
<dl>
{{ form.review(style="padding: 0 0 100px 0; cols='5'; wrap='soft'") }}
</dl>
<div style='padding-left:40px;'>
<input type=submit value='Submit' name='submit_btn'>
</div>
</form>
</body>
</html>
The TextField is actually an input with type="text", which itself doesn't support word wrap. The cols and wrap are not working at all. I think what you need is TextAreaField:
from wtforms import Form, TextAreaField, validators
class ReviewForm(Form):
review = TextAreaField('My Review: ', [validators.DataRequired()])
This will make a textarea that has very good word-wrap support.
Related
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 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') }}
So I am trying to make a form that accepts text when submitted and returns submitted text using the /process function.
Here is my code for index.html:
<!DOCTYPE>
<html>
<head>
<title>Whats my name</title>
<h1>What's my name?</h1>
</head>
<body>
<input type="text">
<form action="POST"
>
<p>your name</p><input type="submit">
</body>
</html>
And here is my Python code:
from flask import Flask, render_template,redirect # Import Flask to allow us to create our app, and import
# render_template to allow us to render index.html.
app = Flask(__name__) # Global variable __name__ tells Flask whether or not we
# are running the file directly or importing it as a module.
#app.route('/')
def home():
return render_template('index.html')
#app.route('/process',methods=['POST'])
def input():
return redirect ('/')
app.run(debug=True)
To retrieve the name value from your html you'll have to add a tag name to the input.
Please see example below, here I named it user_name:
<html>
{...}
<body>
<form action="" method="post">
<input type="text" name="user_name"/>
<p>your name</p>
<input type="submit"/>
</form>
</body>
</html>
Then request the value in your backend Python code
# import the needed request module from Flask
from flask import request
(...)
#app.route('/process', methods=['POST'])
def input():
name = request.form['user_name']
return name
Check this out first: https://www.w3schools.com/tags/att_form_action.asp
Action should be "/process" instead of "POST".
Method is "POST". Also you will need input elements in the form to allow user inputs something.
The input value can be retrieved on the flask side by request.form['value of name attribute of input']
https://www.w3schools.com/tags/tag_input.asp
I would like to recommend you to use https://flask-wtf.readthedocs.io/en/stable/ flask wtf to generate form and retrieve users' input.
I'm trying to display the slider value alongside my wtforms.fields.html5.DecimalRangeField. My current code (relevant extracts below) only renders the slider, with no value. All examples I have seen so far are in pure HTML5 code, and I'm lacking direction on how to do this using using my jinja2 template as a starting point.
Any suggestions?
extract from main.py:
class MyForm(Form):
MyField = DecimalRangeField('Age', [validators.NumberRange(min=1, max=100)])
extract from form.html
<div>{{ wtf.form_field(form.MyField) }}</div>
Try this code (gist):
from flask import Flask, render_template_string
from wtforms import Form
from wtforms.fields.html5 import DecimalRangeField
app = Flask(__name__)
app.config['DEBUG'] = True
TPL = '''
<!DOCTYPE html>
<html>
<head>
<script>
function outputUpdate(age) {
document.querySelector('#selected-age').value = age;
}
</script>
</head>
<body>
<form>
<p>
{{ form.age.label }}:
{{ form.age(min=0, max=100, oninput="outputUpdate(value)") }}
<output for="age" id="selected-age">{{ form.age.data }}</output>
</p>
</form>
</body>
</html>
'''
class TestForm(Form):
age = DecimalRangeField('Age', default=0)
#app.route("/")
def home():
form = TestForm(csrf_enabled=False)
return render_template_string(TPL, form=form)
if __name__ == "__main__":
app.run()
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.