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.
Related
I'm trying to build a very basic app in Flask: a single form that accepts a few inputs from the user (I'm brand new to web development). I was able to get it up and running initially, but when I tried to modify the HTML to include Bootstrap, I get a Not a valid choice error when clicking my submit button.
Main app:
from flask import Flask, render_template, flash, session, redirect, url_for, request
from forms import CustomAnalyticsMenu
app = Flask(__name__)
app.config['SECRET_KEY'] = 'mysecretkey'
#app.route('/')
#app.route('/home')
def home():
return render_template('home.html')
#app.route('/custom_analytics_menu', methods=['GET', 'POST'])
def custom_analytics_menu():
form = CustomAnalyticsMenu()
if form.validate_on_submit():
print("validated_on_submit")
session['menu_item'] = form.menu_item.data
return redirect(url_for('submission'))
else:
print(form.errors)
return render_template('custom_analytics_menu.html', form=form)
#app.route('/submission', methods=['GET', 'POST'])
def submission():
return render_template('submission.html')
if __name__ == '__main__':
app.run(debug=True)
Form:
from flask_wtf import FlaskForm
from wtforms import StringField, RadioField, SelectField, SubmitField
from wtforms.validators import DataRequired
class CustomAnalyticsMenu(FlaskForm):
menu_item = SelectField('Menu Item:', choices=[('option_one', 'Option One'),
('option_two', 'Option Two'),
('option_three', 'Option Three')])
generate_deck = SubmitField('Generate deck')
HTML:
This is the HTML code that works:
{% extends "layout.html" %}
{% block content %}
<div>
<form method="POST" action="">
{{ form.hidden_tag() }}
{{ form.menu_item.label }} {{ form.menu_item }}
{{ form.generate_deck() }}
</form>
</div>
{% endblock %}
However, when I change this portion:
{{ form.menu_item.label }} {{ form.menu_item }}
To this: (which I got from here: https://getbootstrap.com/docs/4.0/components/forms/#form-controls)
<div class="form-group">
<label for="menu_item">{{ form.menu_item.label }}</label>
<select class="form-control" id="menu_item">
{% for item in form.menu_item.choices %}
<option>{{ item[-1] }}</option>
{% endfor %}
</select>
</div>
My SelectField functionally looks the same (except obviously it has the Bootstrap styling), but now when I click the submit button, nothing happens and I get the error Not a valid choice. This is a snippet of what I'm trying to do, I also have a RadioField and a StringField but I get the same errors for those as well when trying to apply similar styling. Can someone help me understand where I'm going wrong?
I think I figured it out! I saw another StackedOverflow question (Why is my flask form validation returning Not a valid choice?) and they had a name field in their <select>. I updated my (value, label) pairs so they matched (i.e. ('Option One', 'Option One') rather than ('option_one', 'Option One')) and updated the code to this:
<div class="form-group">
<label for="menu_item">{{ form.menu_item.label }}</label>
<select class="form-control" name="menu_item">
{% for item in form.menu_item.choices %}
<option>{{ item[-1] }}</option>
{% endfor %}
</select>
</div>
It worked! Alternatively, I could have left the (value, label) pairs the same and used the code from SuperShoot's answer above. I can't say I fully understand why the name tag was needed, so if anyone has an explanation for that, I'd love to hear it.
Edited to include name attrib on <select> field as resolution of additional issue and further explained in #RaghavPaliwal's answer.
The choices that you pass to the SelectField are a a sequence of (value, label) pairs.
In your example these are:
[('option_one', 'Option One'),
('option_two', 'Option Two'),
('option_three', 'Option Three')]
The first element of those tuples is the actual value that the option represents and is meaningful in terms of your application. It needs to be set as the value attribute of the <option> tag. The second element in each tuple is just a nicely formatted representation of the value that the option represents for display to the user.
So, you need to provide a value attribute to each of your <option> tags:
<div class="form-group">
<label for="menu_item">{{ form.menu_item.label }}</label>
<select class="form-control" id="menu_item" name="menu_item">
{% for val, text in form.menu_item.choices %}
<option value="{{ val }}">{{ text }}</option>
{% endfor %}
</select>
</div>
If you don't provide a value attribute to your <option> tags, the content of the tag will be passed as the value of the form field instead. In your case this means that if 'Option One' was selected, the string 'Option One' would be passed as the value of the field. The form validation then tests that it is included in one of the choices values, 'option_one', 'option_two', or 'option_three', so the validation fails.
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.
I'm using Flask 0.12 with Python 3.6 to create a simple app that will display selected items in another page when the submit button is clicked.
The main Flask app is in app.py as:
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/result', methods=['POST'])
def result():
return render_template('result.html')
This renders the following webpage using Bootstrap:
<h1>Example Page</h1>
<p>Choose the options in the form below then submit your selections.</p>
<form action="">
<div class="form-group">
<label for="vehicle">Vehicle</label>
<select id="vehicle" class="form-control">
<option>truck</option>
<option>car</option>
<option>van</option>
</select>
</div>
<div class="form-group">
<label for="year">Year</label>
<select id="year" class="form-control">
<option>1972</option>
<option>1999</option>
<option>2010</option>
</select>
</div>
</form>
<br>
<button type="submit" class="btn btn-default">Submit</button>
How can I get Flask to show the selected items in my results.html template when the submit button is clicked?
You have to make few changes in the form to display in result page.
You have to add action url and method in form
<form action="/result" method="post">
Add name in the select field
<select id="vehicle" class="form-control" name="vehicle">
<select id="year" class="form-control" name="year">
Use flask request to get the form values
from flask import request
# inside your POST view
vehicle = request.form.get('vehicle')
year = request.form.get('year')
return render_template('result.html', vehicle=vehicle, year=year)
Finally in your result html page add these...
{{ vehicle }} {{ year }}
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 %}
What I am trying to achieve:
I am trying to build a very simple admin interface from scratch that stores text fields and images in a mongodb. The user can then login and make small changes to the content of the site.
My issue and where I think I am confused:
Is it possible in pymongo to generate forms with the existing db records showing (so they can be edited and updated in the db)? After trying to understand the problem, I think my confusion lies whether or not I can use WTForms with pymongo directly or if I need to use object based mappers, such as MongoEngine.
Here is my flask app:
app = Flask(__name__)
# define the mongo connection
app.config['DB_HOST'] = 'mongodb://localhost/'
app.config['DB_PORT'] = 27017
app.config['DB_DBNAME'] = 'my_db'
the_db = PyMongo(app, config_prefix='DB')
#others routes here...
#app.route("/admin/")
def dashboard():
pages = list(the_db.db.pages.find({}))
return render_template('admin.html', pages=pages)
#app.route('/admin/update', methods=['POST'])
def update():
updated = datetime.datetime.utcnow()
page = request.form['page_name']
header = request.form['header']
body = request.form['body']
h_db.db.pages.update_one(
{'page': page},
{'$set':
{
'updated': updated,
'header': header,
'body': body
}
}, upsert=True)
pages = list(the_db({}))
return render_template('admin.html', pages=pages)
Here is the template:
{% for i in pages %}
{{ i.page }}<br>
{% endfor %}
{% from "_formhelpers.html" import render_field %}
<form action="/admin/update" autocomplete="on" method="POST">
{{ render_field(form.username) }}
<input type="text" name="page_name" /><br/>
<input type="text" name="header" /><br/>
<input type="TextAreaField" name="body" /><br/>
<input type="submit" name="submit" /><br/>
</form>
This is actually really simple. If you look at the HTML docs for forms then you'll find you can do something like:
<input type="text" name="firstname" value="Mickey"><br>
And "Mickey" will show up in the form for you. So, with flask all you need to do is declare variables in the server code that holds values from your mongodb call for the route and then pass them to the template like you do with "pages" Then in the front end you can render this dynamically with embedded python in Jinja, something like:
#app.route("/admin/<int:page>")
def dashboard(page):
pages = the_db.db.pages.find({page})
for p in pages:
page_name = [p['page']]
header = [p['header']]
body = [p['body']]
return render_template('admin.html', page_name=page_name, header=header, body=body)
Then you can take your template and embed like:
<form action="/admin/update" autocomplete="on" method="POST">
{{ render_field(form.username) }}
<input type="text" name="page_name" value={{ page_name|string }}/><br/>
<input type="text" name="header" value={{ header|string }}/><br/>
<input type="TextAreaField" name="body" value={{ body|string }}/><br/>
<input type="submit" name="submit" /><br/>
</form>
I'm not a WTForms expert, so I'm probably bypassing some helpers that it offers, but for the most part the basics are here. Summarizing:
You need to parse the elements of the MongoDB document and pass them to the template. You can use Jinja's power of dynamically rendering these values to the front-end IN THE APPROPRIATE PLACE as outlined by the HTML standard (the "values" placeholder in the elements). This will get you what you want at it's most basic.
If you want to get fancy you can look at AngularJS to handle the front-end MVC and you can visualize your edits on the fly before submission...sort of like what you see here on SO.com where your post shows up live beneath the edit box.