Having a select option stay selected after POST with Flask - python

I'm trying to get a select option that is selected to stay after the page refresh using Flask. I have attempted to do so with Jinga2, but it's is not working:
<div class="col-sm-4 col-lg-4 col-md-4">
<select class="form-control" id="myselect" name="thing" required>
<option value="" {% if thing=='' %} selected {% endif %} ></option>
<option value="Foo" name="Foo" id="Foo" {% if thing =="Foo" %} selected {% endif %}>Foo</option>
<option value="Bar" name="Bar" id="Bar" {% if thing =="Bar" %} selected {% endif %}>Bar</option>
</select>
</div>
Where the variable energy is populated and passed through with Python. After looking into this, I feel that this is the way to make this work in Flask, though apparently not. Any assistance would be appreciated!
#app,route('/things', methods=['POST']
def things()
if len(facts['thing']) > 11:
energy = [facts['thing'][0:8],facts['thing'][9:]]
else:
energy = [facts['things']]
...
return render_template('thing.html', thing=energy)

Please see this example as it works for what you're trying to do. I can't exactly debug what's going wrong in your code because you've provided me with parts and I don't know what they're doing.
Folder structure
Test
|___templates
| |___things.html
|___Test.py
things.html
<form method="post">
<div class="col-sm-4 col-lg-4 col-md-4">
<select title="thing" class="form-control" id="myselect" name="thing" required>
<option value="" {% if thing=='' %} selected {% endif %} ></option>
<option value="Foo" name="Foo" id="Foo" {% if thing =="Foo" %} selected {% endif %} >Foo</option>
<option value="Bar" name="Bar" id="Bar" {% if thing =='Bar' %} selected {% endif %}>Bar</option>
</select>
<button type="submit">SEND</button>
</div>
</form>
Test.py
from flask import Flask, render_template, request
app = Flask(__name__)
PORT = 5000
#app.route('/things', methods=['GET', 'POST'])
def things():
"""
Accepts both GET and POST requests. If it's a GET request,
you wouldn't have a last selected thing, so it's set to an
empty string. If it's a POST request, we fetch the selected
thing and return the same template with the pre-selected
thing.
You can improve on this and save the last selected thing
into the session data and attempt to retrieve it from there.
"""
thing = ''
if request.method == 'GET':
return render_template('things.html', thing=thing)
else:
thing = request.form.get('thing', '')
return render_template('things.html', thing=thing)
if __name__ == '__main__':
app.run(port=PORT)

The important part is rendering the page with selected in your desired option:
<option value="Foo" selected>Foo</option>
double_j's answer to use templates to insert it into your html works great, but if you're building the dropdown from a list it may be easier to build your html from python:
import flask
import socket
app = flask.Flask(__name__)
all_things = ['Foo', 'Bar', 'Fizz', 'Buzz']
current_thing = None
def show_selection(target):
if target == current_thing:
return 'selected'
else:
return ''
def menu():
template = '''<form action = "things" method = "post">
<select id="target" name="target">
{targets}
</select>
<input type="submit" name="build" value="Build">
</form>
'''
# vvv This is the important part vvv
targets = [f'<option value="{t}" {show_selection(t)}>{t}</option>' for t in all_things]
return template.format(
targets='\n'.join(targets)
)
# ^^^ This is the important part ^^^
#app.route('/things', methods=['GET', 'POST'])
def things():
global current_thing
current_thing = flask.request.form.get('target')
page = menu()
if flask.request.method == 'POST' and 'build' in flask.request.form:
page += f'Building {current_thing}'
else:
page += 'Press Build button'
return page
if __name__ == '__main__':
PORT = 8080
print("Visit http://{}:{} to trigger builds".format(socket.gethostname(), PORT))
app.run(host='0.0.0.0', port=PORT, debug=True)

try this, if you have not already
{% if thing == "Foo" %}
<option value = "Foo" name ="Foo" id="Foo" selected>Foo</option>
<option value = "Bar" name ="Bar" id="Bar">Bar</option>
{% elif thing == "Bar" %}
<option value = "Foo" name ="Foo" id="Foo">Foo</option>
<option value = "Bar" name ="Bar" id="Bar" selected>Bar</option>
{% endif %}

Related

Why is my flask application following the error route?

I am trying to write a code (CS50) that uses flask and HTML and I am supposed to create a server where you can input your name as well as a provided option. After this, the results are displayed in a table, the file is called registration.html, (using HTML) as well as recorded in a SQL database.
This is the code for app.py
from cs50 import SQL
from flask import Flask, redirect, render_template, request
app = Flask(__name__)
db = SQL("sqlite:///froshims4.db")
OPTIONS = [ "Stochastic Calculus",
"Financial Engineering",
"Statistical Sciences",
"Algorithmic Progression Systems",
"Econometrics"]
#app.route("/")
def index():
return render_template("index.html", options=OPTIONS)
#app.route("/register",methods=['POST'])
def register():
name = request.form.get("name")
option = request.form.get("option")
if not name or option not in OPTIONS:
return render_template("error.html")
db.execute("INSERT INTO registrants (name,option) VALUES (?,?)",name,option)
return redirect ('/registrants')
#Flask includes a redirect function which redirects to another route.
#app.route("/registrants")
def registrants():
registrants = db.execute("SELECT * from registrants ")
return render_template("registrants.html", registrants=registrants)
This is the code for index:
{% extends "layout.html" %}
{% block body %}
<h1>Register</h1>
<form action="/register" method="post">
#we plan to create a register route
<input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
<!--a select menu is sort of like a drop down menu <select name="sport"> <option disable selected>Sport</option> -->
<!-- A radiobutton is mutually exclusive checkbox where the user can sign up for only one option-->
{% for option in options %}
<input name="option" type="checkbox" value="'{{option}}">{{option}}
{% endfor %}
</select>
<input type="submit" value="Register">
</form>
{% endblock %}
app.py directs me to error.html even if I have entered a name and a feasible option. Why is this? How can I fix it? Thank you in advance!
<input name="option" type="checkbox" value="'{{option}}">{{option}}
You have an extra single-quote inside value=. So the actual value being returned is e.g. 'Stochastic Calculus which does not match any value in the OPTIONS list.
Also you don't have a closing </option> tag.

switch/toogle results in BadRequestKeyError: 400 Bad Request

I have an index.html generated with python & flask that shows a list. I want to filter this list by a bootstrap switch. Now I have the following code and it works to show my index.html list by GET request. enabling the switch also works and shows the filtered list. But when switching back to unfiltered/off I receive a bad request. the problem is with switchvalue = request.form['switch'] but I dont understand why this is.
Python code:
#app.route('/', methods=['POST', 'GET'])
def index():
conn = get_db_connection()
if request.method == 'POST':
switchvalue = request.form['switch']
flash(switchvalue)
if switchvalue == '1':
rows = conn.execute('SELECT Name, CAST (Points AS int) as Points, isActive FROM table WHERE isActive = "Active"').fetchall()
conn.close()
return render_template('index.html', rows=rows, switchcheck=1)
rows = conn.execute('SELECT Name, CAST (Points AS int) as Points, isActive FROM table').fetchall()
conn.close()
return render_template('index.html', rows=rows, switchcheck=0)
HTML:
...
{% block content %}
<h1>{% block title %} Title {% endblock %}</h1>
<form method="POST" action="{{ url_for('index') }}">
<div class="custom-control custom-switch">
{% if switchcheck == 0 %}
<input type="checkbox" name="switch" onclick=this.form.submit() value="1" class="custom-control-input" id="customSwitch1">
{% else %}
<input type="checkbox" name="switch" onclick=this.form.submit() value="0" class="custom-control-input" id="customSwitch1" checked>
{% endif %}
<label class="custom-control-label" for="customSwitch1">Active</label>
</div>
</form>
{% for row in rows %}
...
So it seems like the value of switch does not get POSTed because switches are handled as checkboxes and when they are not checked they dont submit value. therefore submit.form(switch) doesnt see data.
I found my hint here: How to utilize Bootstrap Ti Ta Toggle Checkbox with Flask
Also there is mentioned to use hidden form with name=switch to handle this problem but was no success for me. My workaround looks as follows:
try:
switchvalue = request.form['vrswitch']
except:
switchvalue = 0
I bet there are more elegant ways to do this but it works!

How to pass a jinja variable into flask using url_for

I have a form in HTML that looks like this:
<form action="{{ url_for('home_blueprint.tabledata', tablename = col )}}" method="post">
<select name="tables" placeholder="Table" id="tables" size=3>
{% for col in column_names %}
<option value = "{{ col }}">{{ col }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-primary btn-block btn-large">Submit</button>
</form>
Basically, the form above displays a drop-down list of items {{ col }}. I want that a selected and submitted item from the list above (for example: 'tablename' ) appears in my URL like www.example.com/tabledata/tablename
My flask code is here:
#blueprint.route('/tabledata/<tablename>', methods=['GET', 'POST'])
#login_required
def tabledata(tablename):
return render_template('index.html', tablename=tablename)
In other words, I want to pass a variable from this form to flask using url_for.
How I do that? Thank you in advance!!
There are many ways to do this. I'll go with the most basic one (at least for me).
You just need to tweak your function so if someone submits a GET request, he/she gets the dropdown list, and if the user submits a POST request, fetch the POST data and redirect him/her to another url, the dynamic one.
So your flask code becomes:
#blueprint.route('/tabledata/<tablename>', methods=['GET', 'POST'])
#login_required
def tablename(tablename):
return "Hi"
#blueprint.route('/tabledata')
#login_required
def tabledata():
if request.method == 'GET':
return render_template('index.html', column_names=['abc', 'def', 'ghi']) # Column names were added by me, ignore it
elif request.method == 'POST':
data = request.form.get("tables")
return redirect(url_for('tablename', tablename=data))
And just a little change to the form action:
<form action="{{ url_for('home_blueprint.tabledata')}}" method="POST">
<select name="tables" placeholder="Table" id="tables" size=3>
{% for col in column_names %}
<option class="column" value="{{ col }}">{{ col }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-primary btn-block btn-large">Submit</button>
</form>
Now if you click on an option, say 'abc', from the dropdown, and submit it, you will be taken to the url www.example.com/tabledata/abc .

Getting None from request.form.get()

I need to get data from 2 different forms. The first "index.html" rendering takes a user's selected option and stores it on the variable "item".
on "page2.html", the "item" value is shown here just fine. And again, the user is entering some new data on the textbox input and then it's redirected to the next rendering, "page3.html"
on "page3.html", it is supposed to show both data from the forms on "index.html" and "page2.html" but i'm getting "None" when I try to show the "item" value. Why?
I've tried to do it on separate app.route() routes, but I got the None type value as well.
the app.py file:
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route("/", methods=['GET', 'POST'])
def index():
if request.method == 'POST':
if request.form.get('send') == 'send':
item = str(request.form.get('test1'))
return render_template('page2.html', item=item)
if request.form.get('send2') == 'send2':
item = str(request.form.get('test1'))
text = request.form.get('textbox')
return render_template('page3.html', item=item, text=text)
else:
return render_template('index.html')
app.run(debug=True)
the index.html file:
<div align="center">
<form method="POST" action="{{ url_for('index') }}">
<select name="test1">
<option name="1" value="1">1</option>
<option name="2" value="2">2</option>
<option name="3" value="3">3</option>
</select>
<button type="submit" name="send" value="send">Send</button>
</form>
</div>
the page2.html file:
<div align="center">
<form method="POST" action="{{ url_for('index') }}">
<h2>Previous selection: {{ item }}</h2><br>
<input type="text" name="textbox" value="Enter text">
<button type="submit" name="send2" value="send2">Send</button>
</form>
</div>
and the page3.html file:
<div align="center">
<h2>Your result is:</h2><br>
<h2>"{{ text }}" and "{{ item }}" </h2>
</div>
store the value of item in a session so that you can access it later. Once you are done you can clear the session like session.pop("session_name", None)
if request.form.get('send') == 'send':
item = str(request.form.get('test1'))
session["test1"]=item
return render_template('page2.html', item=item)
if request.form.get('send2') == 'send2':
item = session.get("test1")
text = request.form.get('textbox')
return render_template('page3.html', item=item, text=text)

Why I am getting Bad request error for this flask page?

I have two flask form in same page. One form is visible at the beginning and user have to select an option and based on that the second form is generated. Second from contains a dropdown which will submit the form on onchange event. When this submission occurs I am getting bad request error. My Html form is:
<form action="/ip" method="POST" name="btn" value="project">
<label>Select Project : </label>
<select class="form-control" style="width: 50%;display:inline-block" name="project_name">
<option></option>
<option value="k">Ki</option>
<option value="s">S</option>
<option value="l">L</option>
</select>
<button type="submit" class="btn btn-primary m-b-10 m-l-5" style="display:inline-block" ">Fetch Details</button>
</form>
<form method="POST" action="/ip" name="btn" value="dlvr">
<select id="subsystem_value" onchange="this.form.submit()">
{% for i in data %}
{% for k in i %}
<option value={{ k }}>{{ k }}</option>
{% endfor %}
{% endfor %}
</select>
</form>
and my flask view is :
#auth.route('/ip',methods = ['POST', 'GET'])
def ip():
if request.method == 'POST':
if request.form['btn'] == "project":
project = request.form['project_name']
c, conn = connection()
subsystem = "SELECT distinct sub from ip where project='{}'".format(project)
query = "SELECT distinct del from ip where project='{}'".format(project)
c.execute(query)
data = c.fetchall()
c.execute(subsystem)
subsystem = c.fetchall()
sub = []
for row in subsystem:
for id in row:
sub.append(id)
conn.close()
return render_template('ip.html', data=data,sub=sub)
else:
subsystem = request.form['subsystem_value']
return render_template('ip.html')
else:
return render_template('ip.html')
I tried to make same name for forms and gave separate values too. But still I am getting bad request error.
I have two areas where your error is coming from
1) Form name is deprecated since HTML 4 and is no longer submitted therefore
if request.form['btn'] == "project":
will not be understood by flask
The way to go around that is to give your submit button a name like this
<button type="submit" name="project" class="btn btn-primary m-b-10 m-l-5" style="display:inline-block" ">Fetch Details</button>
and in your flask view, check for the form like this
if 'project' in request.form:
2) The second error is on this line
<form method="POST" action="/ip" name="btn" value="dlvr">
<select id="subsystem_value" onchange="this.form.submit()">
line two here should be name= "subsystem_value", not id="subsystem_value"
otherwise this line in flask will not be understood
subsystem = request.form['subsystem_value']
So I have this simplified code that is no longer giving me an error and returning the values the way I think you want them
in flask
#app.route('/ip',methods = ['POST', 'GET']) #Used #pp.route to match my testing app
def ip():
if request.method == 'POST':
if 'project' in request.form:
project = request.form['project_name']
return str(project)
else:
subsystem = request.form['subsystem_value']
return str(subsystem)
else:
return render_template('test.html')
in the template
<form action="/ip" method="POST" >
<label>Select Project : </label>
<select class="form-control" style="width: 50%;display:inline-block" name="project_name">
<option></option>
<option value="k">Ki</option>
<option value="s">S</option>
<option value="l">L</option>
</select>
<button type="submit" name="project" class="btn btn-primary m-b-10 m-l-5" style="display:inline-block" ">Fetch Details</button>
</form>
<form name method="POST" action="/ip" >
<select name="subsystem_value" onchange="this.form.submit();">
<option value="test1">t1</option>
<option value="test2">t2</option>
<option value="test3">t3</option>
</select>
</form>
Hope that helps you out
You are in the perfect position to avail yourself of Flasks debug facilities, which you can activate by either setting the environment variable FLASK_DEBUG=1 or by passing debug=True to app.run().
See http://flask.pocoo.org/docs/1.0/api/#flask.Flask.debug
Also, if there's any chance that this code will face hostile users, use SQL bind variables instead of constructing a query from untrusted input. A SQL Injection attack can ruin your day.

Categories