Displaying slider value alongside wtforms.fields.html5.DecimalRangeField - python

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()

Related

Flask/HTML question: Where does a request from an input text box in html arrive in app.route in flask? [duplicate]

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>

How can I pass a value from a drop down list to another page in flask

I want to have a page where an option is selected from a drop down list that is passed to the next page. The error I receive is "UnboundLocalError: local variable 'currentuser' referenced before assignment". I'm not sure how to update the variable globally when an option is selected from the drop down list or how to access the global variable locally in the next page function. I am new to python and flask, any help would be greatly appreciated!
app.py
from flask import Flask, render_template
import sqlite3
app = Flask(__name__)
#app.route('/selectusername')
def selectusername_page():
# connect to database and populate userlist
conn = sqlite3.connect('users.db')
c = conn.cursor()
c.execute("SELECT * FROM users")
userlist = c.fetchall()
conn.close()
return render_template('selectusername.html', userlist=userlist)
#app.route('/showusername')
def showusername_page():
currentuser=currentuser
return render_template('showusername.html', currentuser=currentuser)
if __name__ == '__main__':
app.run(debug=True)
selectusername.html
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<body>
<button onclick="window.location.href = 'showusername';">Continue</button>
<h1>Select User</h1>
<select id="currentuser">
{% for user in userlist %}
<option value="{{user[0]}}">{{user[0]}}</option>
{% endfor %}
</select>
</body>
</html>
showusername.html
<h1>Hello {{ currentuser }}</h1>
If you use
<form action="/showusername">
and button without JavaScript and you use name="currentuser" in <select>
<select name="currentuser">
then it can send selected value in url
/showusername?currentuser=selected_name
and you can get it in showusername using request.args
currentuser = request.args.get("currentuser")
To hide name from url you would have to use POST method - so you have to set
<form action="/showusername" method="POST">
and in flask
#app.route('/showusername', methods=['POST', 'GET'])
and then you get it using request.form instead of request.args
currentuser = request.form.get("currentuser")
Full running example
from flask import Flask, render_template, render_template_string, request
app = Flask(__name__)
#app.route('/selectusername')
def selectusername_page():
userlist = [['James'], ['Adam'], ['Mark']]
return render_template_string('''<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<body>
<form action="/showusername">
<button>Continue</button>
<h1>Select User</h1>
<select id="currentuser" name="currentuser">
{% for user in userlist %}
<option value="{{user[0]}}">{{user[0]}}</option>
{% endfor %}
</select>
</form>
</body>
</html>''', userlist=userlist)
#app.route('/showusername', methods=['POST', 'GET'])
def showusername_page():
print('args:', request.args)
print('form:', request.form)
#currentuser = request.args.get("currentuser")
currentuser = request.form.get("currentuser")
return render_template_string('''<h1>Hello {{ currentuser }}</h1>''', currentuser=currentuser)
if __name__ == '__main__':
app.run(debug=True)
If you want to use JavaScript in button then you would have to use JavaScript to get selected value and add it to url like
window.location.href = 'showusername?currentuser=selected_name'
so it is more complicated and I don't put code in JavaScript. Maybe someone else will show this.

Python global variable not updating in real time flask

value=user_id is not taking the new value defined by def login():.for e.g. if the entered user_id from HTML page is 200, 300 and 100. The values will be assigned as 0, 200, 300. That is, when home page is displayed it will be showing user 0 for entered value 200, and for second time it is showing 200 for entered value 300 and for third time it is showing 300 for entered value 100.
I need a real time values i.e. for entered value 200 home page should have value 200, and for 300 it should be 300. Could anyone please help me with this? Please advice. Thanks in advance. Note: HTML code is not optimized.
from flask import Flask, render_template # Flask is the class within the flask library
from flask import request, redirect
user_id = int(0)
app = Flask(__name__) # Instance of the object Flask. __name__: this gets value of name of python script
#app.route('/', methods=["GET", "POST"]) # login page
def login():
if request.method == "POST":
global user_id
user_id = request.form['userid']
print(user_id)
return render_template("login.html")
#app.route('/home/') # This the URL i.e. home page
def home():
return render_template("home.html", value=user_id)
if __name__ == "__main__":
app.run(debug=True)
HTML Code for Login page here.
<!DOCTYPE html>
<html>
<head>
<title>Recommender System App</title>
<link rel="stylesheet" type="text/css" href="{{url_for('static',filename='css/main.css')}}">
</head>
<body>
<header>
<div class="container">
<h1 class="logo">Movie Recommender System</h1>
<strong><nav>
<ul class="menu">
</ul>
</nav></strong>
</div>
</header>
<form action="{{ url_for('login') }}" method="post">
<div class = "Login Fields">
<b>Username:</b> <input type="number" placeholder="Numbers only" name='userid'>
<p><b>Password:</b> <input type="password" placeholder="Enter Password" name= 'password' id="pwd"></p>
<input type="submit" onclick="check(this.form)" class="button">
</div>
<p></p>
<p></p>
<div>
<p>Test User IDs:</p>
<li>224</li>
<li>216</li>
<li>681</li>
<li>19</li>
<li>82</li>
<li>305</li>
<li>44</li>
<li>268</li>
<p>Password: 123Swaroop</p>
</div>
</form>
<script language="javascript">
function check(form)/*function to check userid & password*/
{
if(form.password.value == "123Swaroop")
{
window.open(href = "{{ url_for('home') }}")
}
else
{
alert("Wrong Password or User Id")/*displays error message*/
}
}
</script>
</body>
</html>
HTML code for home page here:
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<link rel="stylesheet" type="text/css" href="{{url_for('static',filename='css/main.css')}}">
</head>
<body>
<header>
<div class="container">
<h1 class="logo">Welcome User: {{ value }}</h1>
<strong><nav>
<ul class="menu">
</ul>
</nav></strong>
</div>
</header>
<div class = "home">
<h1>Top Rated Movies</h1>
<p>This is test website</p>
</div>
</body>
</html>
You also need to call the global value within your home() function:
from flask import Flask, render_template, request, redirect
user_id = int(0)
app = Flask(__name__)
#app.route('/', methods=["GET", "POST"])
def login():
if request.method == "POST":
global user_id
user_id = request.form['userid']
print(user_id)
return render_template("login.html")
#app.route('/home/')
def home():
global user_id
return render_template("home.html", value=user_id)
if __name__ == "__main__":
app.run(debug=True)
However, using global variables is usually considered bad practice so you could also consider something like this:
from flask import Flask, render_template, request, redirect
app = Flask(__name__)
#app.route('/', methods=["GET", "POST"])
def login():
return render_template("login.html")
def get_user_id():
try:
if request.method == "POST":
user_id = request.form['userid']
return user_id
else:
# Handle error or do something else
except:
# Handle error or do something else
#app.route('/home/')
def home():
try:
return render_template("home.html", value=get_user_id())
except:
# Handle error or do something else
if __name__ == "__main__":
app.run(debug=True)

Line wrapping in HTML forms using Flask

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.

Select2 field implementation in flask/flask-admin

I'm trying to implement Select2 field in one of my flask views. Basically I want the same select2 field in my flask application view (not a flask admin modelview) as in Flask-admin model create views. Currently my solution has been QuerySelectField from wtforms that looks something like this
class TestForm(Form):
name= QuerySelectField(query_factory=lambda: models.User.query.all())
This allows me to load and select all the data I need, but it does not provide select2 search box etc. Currently all that I have found is Select2Field and Select2Widget from flask/admin/form/fields and flask/admin/form/widgets similarly like in this post https://stackoverflow.com/questions/24644960/how-to-steal-flask-admin-tag-form-field and also select2 documentation at http://ivaynberg.github.io/select2/
As I understand these could be reusable, meaning there is no need for other custom widgets, custom fields.
Would be grateful if someone could provide more information about select2 field implementation in flask application (including views, templates, forms files and how to correctly "connect" with neccessary js and css files, also how to load the field up with the database model I need).
This worked for me:
...
from wtforms.ext.sqlalchemy.fields import QuerySelectField
from flask_admin.form.widgets import Select2Widget
...
class TestForm(Form):
name= QuerySelectField(query_factory=lambda: models.User.query.all(),
widget=Select2Widget())
And in your template:
{% extends "admin/master.html" %}
{% import 'admin/lib.html' as lib with context %}
{% block head %}
{{ super() }}
{{ lib.form_css() }}
{% endblock %}
{% block body %}
...
{% endblock %}
{% block tail %}
{{ super() }}
{{ lib.form_js() }}
{% endblock %}
I can try to put together a minimal working example if necessary.
I had a similar requirement and have put together a minimal example.
Note the following:
Class TestView defines three routes; a get view, a post view and an Ajax lookup view.
Function get_loader_by_name takes a string name and returns a QueryAjaxModelLoader. This function is used in both the Ajax lookup call and in the TestForm field definitions.
The text displayed in the Select2 widgets is the value returned by the User model's __unicode__ method.
I've used Faker to generate the sample user data.
File app.py:
from flask import Flask, render_template, url_for, request, abort, json, Response
from flask.ext.admin import Admin, BaseView, expose, babel
from flask.ext.admin.contrib.sqla.ajax import QueryAjaxModelLoader
from flask.ext.admin.model.fields import AjaxSelectField, AjaxSelectMultipleField
from flask.ext.sqlalchemy import SQLAlchemy
from wtforms import Form
from faker import Factory
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'super-secret'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
try:
from flask_debugtoolbar import DebugToolbarExtension
DebugToolbarExtension(app)
except:
pass
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.Unicode(length=255), nullable=False)
last_name = db.Column(db.Unicode(length=255), nullable=False)
email = db.Column(db.Unicode(length=254), nullable=False, unique=True)
def __unicode__(self):
return u"{first} {last}; {email}".format(first=self.first_name, last=self.last_name, email=self.email)
def get_loader_by_name(name):
_dicts = {
'user': QueryAjaxModelLoader(
'user',
db.session, User,
fields=['first_name', 'last_name', 'email'],
page_size=10,
placeholder="Select a user"
)
}
return _dicts.get(name, None)
class TestView(BaseView):
def __init__(self, name=None, category=None,
endpoint=None, url=None,
template='admin/index.html',
menu_class_name=None,
menu_icon_type=None,
menu_icon_value=None):
super(TestView, self).__init__(name or babel.lazy_gettext('Home'),
category,
endpoint or 'admin',
url or '/admin',
'static',
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self._template = template
#expose('/', methods=('GET',))
def index_view(self):
_form = TestForm()
return self.render(self._template, form=_form)
#expose('/', methods=('POST',))
def post_view(self):
pass
#expose('/ajax/lookup/')
def ajax_lookup(self):
name = request.args.get('name')
query = request.args.get('query')
offset = request.args.get('offset', type=int)
limit = request.args.get('limit', 10, type=int)
loader = get_loader_by_name(name)
if not loader:
abort(404)
data = [loader.format(m) for m in loader.get_list(query, offset, limit)]
return Response(json.dumps(data), mimetype='application/json')
# Create admin and Test View
admin = Admin(app, name='Admin', template_mode='bootstrap3')
admin.add_view(TestView(template='test.html', name="Test", url='/test', endpoint='test'))
class TestForm(Form):
single_user = AjaxSelectField(
loader=get_loader_by_name('user')
)
multiple_users = AjaxSelectMultipleField(
loader=get_loader_by_name('user')
)
#app.route('/')
def index():
return render_template("index.html", link=url_for('test.index_view'))
def build_db():
db.drop_all()
db.create_all()
fake = Factory.create()
for index in range(0, 1000):
_first_name = fake.first_name()
_last_name = fake.last_name()
_user_db = User(
first_name=_first_name,
last_name=_last_name,
email="{first}.{last}{index}#example.com".format(first=_first_name.lower(), last=_last_name.lower(), index=index)
)
db.session.add(_user_db)
db.session.commit()
#app.before_first_request
def before_first_request():
build_db()
if __name__ == '__main__':
app.debug = True
app.run(port=5000, debug=True)
File templates/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test Select2</title>
</head>
<body>
Go to the test form
</body>
</html>
File templates/test.html:
{% import 'admin/static.html' as admin_static with context %}
{% import 'admin/lib.html' as lib with context %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test Select2</title>
<link href="{{ admin_static.url(filename='bootstrap/bootstrap3/css/bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='bootstrap/bootstrap3/css/bootstrap-theme.min.css') }}" rel="stylesheet">
<link href="{{ admin_static.url(filename='admin/css/bootstrap3/admin.css') }}" rel="stylesheet">
{{ lib.form_css() }}
</head>
<body>
<div class="container">
<div class="row">
<div class="col-sm-10 col-sm-offset-2">
<form>
{{ lib.render_form_fields(form) }}
</form>
</div>
</div>
</div>
<script src="{{ admin_static.url(filename='vendor/jquery-2.1.1.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='bootstrap/bootstrap3/js/bootstrap.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='vendor/moment-2.8.4.min.js') }}" type="text/javascript"></script>
<script src="{{ admin_static.url(filename='vendor/select2/select2.min.js') }}" type="text/javascript"></script>
{{ lib.form_js() }}
</body>
</html>
Update July 2018
Added a standalone Flask extension available on Github - Flask-Select2 - WIP.
I recently implemented a "tags" field in the front-end of a Flask app, using Select2 and WTForms. I wrote a sample app, demonstrating how I got it working (the view code for populating the select options, and for dynamically saving new options, is where most of the work happens):
https://github.com/Jaza/flasktaggingtest
You can see a demo of this app at:
https://flasktaggingtest.herokuapp.com/
No AJAX autocomplete in my sample (it just populates the select field with all available tags, when initializing the form). Other than that, should be everything that you'd generally want for a tagging widget in your Flask views / templates.
Other answers seem not to work anymore. What I did to make it work is:
On the backend:
from wtforms.ext.sqlalchemy.fields import QuerySelectField, QuerySelectMultipleField
...
users = users = QuerySelectMultipleField('users', query_factory=lambda: User.query.order_by(User.fullname).all(),
get_label=lambda u: u.fullname, get_pk=lambda u: u.id)
Then in the frontend:
$("#users").attr("multiple", "multiple").select2();
You need to manually add css and js for select2.

Categories

Resources