
Welcome!
Everything is fine.
Flask Part 3¶
Today, we'll continue to work with Flask, finishing all the essential pieces to create a working database app, particularly the request object.
- Goal
- Plan
- Announcements
- Assignment Demo
- Exam Stuff
- Recap for today
- Where is my Data?
- Questions for Today
- Starting
- Today's Examples
- TO DO
- Example: template inheritance
- Example: request_method (sqrt)
- Redirects
- Flashing
- Example of Flashing
- Breakout Exercise: People Born in Month
- Part 1 the Form
- Part 2 Processing the Form
- Part 3 Redirect
- Part 4 Flashing
- Solution
- Summary
Goal¶
By the end of today, you will be able to use redirects and flashing in Flask applications. You'll be completely prepared for H6 Lookup and almost prepared for H7 CRUD.
Plan¶
- Announcements
- Forms in Flask: the
request
object - redirects
- flashing
- your questions
- Breakout: form to specify the birth month for search
Next time:
- POST-Redirect-GET pattern
- other form patterns
Announcements¶
- H6 Lookup
- Template Inheritance is not required (but could be useful). It's sufficient to put the form on the main page
- POST is not required (form uses GET)
- Redirects are required
- You'll handle the form submission, possibly, redirecting to the authoritative page on a single movie or person.
Assignment Demo¶
Exam Stuff¶
I will briefly review the solutions to the two parts of the exam (but not the retakes), and hand out my solutions.
Recap for today¶
- GET vs POST:
- like a postcard vs a parcel
request.args
vsrequest.form
- both are dictionary-like, so you can use
request.args[key]
orrequest.form.get('key')
- Rendering a template and passing in data using the keyword arguments to
render_template
render_template('template-file.html', name_in_file = local_variable, ...)
- Redirect
- response is not a page, but a URL that the browser is requested to GET instead of this one
- should be done for clean interface to the app, rather than the convenience of the programmer
- Example: redirect from a search form to a response page:
https://www.imdb.com/name/nm0000123/
- Example: redirect from a login form to a "home page":
/myapp/<username>
- Example: redirect from an order form to a response page:
/myapp/confirmation/<orderid>
- The last example is also an example of POST-REDIRECT-GET, which helps to avoid problems with, for example, re-ordering if the user refreshes a page. By redirecting them, you put them on a page that is safe to refresh, while the original order form page is not.
- Flashing:
- A useful standardized way to get messages displayed to the user.
- Have to set a secret key:
app.secret_key='some string'
- Use a function to squirrel away a message to be displayed:
flash('some message')
- Have to put a mechanism in the templates to display the squirreled-away message:
for msg in get_flashed_messages()
- There's a few standard incantations for the latter.
- Template Inheritance
- A good way to have a theme and variations
- Put stuff you want in every page in a base template
- Define child variations to inherit from the base
- It's common to put the flashing incantation in the base.
- The base looks like a normal Jinja2 template, except override-able blocks are marked.
- The children just list the base and their overrides to one or more blocks
Where is my Data?¶
- if the data in the endpoint, before the question mark,
- get it from the function arguments
- if the data is in the query string, the part after the question mark, (such as a form using GET),
- get it from
request.args
- get it from
- if the data is in the body of the request, as with a form using POST,
- get it from
request.form
- get it from
Questions for Today¶
Lots of uncertainty on this material, so we'll take our time.
This is a good time to answer your quiz questions.
Starting¶
To start our work for today, make sure you activate your virtual environment:
source ~/cs304/venv/bin/activate
Today's Examples¶
Copy the flask3
folder from the course account to your own, and you can
re-create all of today's examples:
cd ~/cs304/
cp -r ~cs304/pub/downloads/flask3 flask3
cd flask3
Guide: That folder has these files. We'll punt many of those until next time.
all_examples.py
defines theapp
object, and imports the examples. So, we run it, but read the code in other files.example_template_inheritance.py
is our first example, showing route that each use a different template, but the latter two inherit from the base template.example_request_method.py
is our first form example, showing different request methods.example_form_processing_one_route.py
is our second form example, showing GET to get an empty form and POST to use it.example_post_redirect_get2.py
is our third form example, showing POST that redirects to a GET route to do the real work.example_flashing.py
is an example of flashing to report errors in an easy way.
TO DO¶
I will run the all_examples.py
app in the guest account. You're welcome
to do the same with your copy. We'll look at the code using VSC.
Example: template inheritance¶
For that, we'll skip ahead to next time.
Example: request_method (sqrt)¶
As you know, forms are critically important in Web applications. Our
first example is example_request_method.py
, which also refers to
templates/sqrt_form.html
and templates/msg.html
.
<!doctype html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<title>{{title}}</title>
</head>
<body>
<h2>GET</h2>
<form method="GET" action="{{processor}}">
<p><label>input number
<input name="num" type="number" step="any">
</label></p>
<p><input type="submit" value="GET"></p>
</form>
<h2>POST</h2>
<form method="POST" action="{{processor}}">
<p><label>input number
<input name="num" type="number" step="any">
</label></p>
<p><input type="submit" value="POST"></p>
</form>
<h2>Menu</h2>
<form method="GET" action="{{processor}}">
<p><label>select number
<select name="num">
<option value="2">two</option>
<option value="3">three</option>
<option value="3.1416">pi</option>
<option value="4">four</option>
</select>
</label></p>
<p><input type="submit" value="submit"></p>
</form>
</body>
</html>
Notice:
- There can be more than one form on a page
- They can have different methods. Note the difference in how the data is sent.
- Notice the different behavior on re-submission. (Click the reload button.)
- the ACTION needs to be filled in. Typically I put this in the template, rather than in the route.
- How to use a SELECT menu
- We could use
url_for(...)
in the template, where we have{{processor}}
if it'll always go to the same place, which is typical. I separated them to make the Python code clearer.
Here is the Flask code to process it:
from flask import (Flask, url_for, render_template, request,
redirect, flash)
import math
from all_examples import app
@app.route('/sqrt_form/')
def sqrt_form():
# This route just returns the form.
return render_template('sqrt_form.html',
processor=url_for('compute_sqrt'))
@app.route('/compute_sqrt/', methods=['GET','POST'])
def compute_sqrt():
# the request object is a new import from flask
if request.method == 'GET':
num = request.args.get('num')
else:
num = request.form.get('num')
try:
x = float(num)
y = math.sqrt(x)
return render_template(
'msg.html',
msg=('Square root of {x} is {y}'
.format(x=x,y=y)))
except:
return render_template(
'msg.html',
msg=('Error computing square root of {num}'
.format(num=num)))
- The
request
symbol needs to be imported request.method
is either GET or POSTrequest.args
goes with GETrequest.form
goes with POST- Look at the URL for the different options
Redirects¶
A redirect is a short response to the browser telling it to make a different request. You can say
- 301 moved permanently
- 302 found (temporarily)
- Many others. Check HTTP Status Codes
Redirects can be used for a variety of reasons.
- To clean up a URL using query parameters, converting:
/find?q=123
to/name/nm123
- To transfer someone to a more appropriate page:
/login?name=fred&pass=ok
/user/fred
You don't want to over-use redirects. It should be more about what the user sees rather than convenient coding.
As with render_template()
you return
the redirect:
return redirect( url_for('index') )
Also, you have to import the redirect
function; see below.
Flashing¶
A web application should always let the user understand what's going on:
- Confirmation of updates
- Show search criteria as well as search results
- Appropriate error messages
You can do this by inserting information on the page:
{% if errmsg %}
<p>{{ errmsg }}</p>
{% endif %}
A more general solution is to have a collection of messages that you can iterate over to show them all. Like this:
{% with messages = get_flashed_messages() %}
{% if messages %}
<div id="messages">
{% for msg in messages %}
<p>{{msg}}</p>
{% endfor %}
</div>
{% endif %}
{% endwith %}
How you format the messages, of course, is up to you. Here, we've used a bunch of paragraphs in a DIV. Style it with CSS.
To add a message to the list, use the flash()
function:
flash('actor inserted')
You have to remember to import the flash
function into your app. See
below.
Flask uses a special kind of cookie to implement sessions and flashing, thereby allowing the flashed messages to go across re-directs. We'll discuss the implementation later. However, it's important to know that you have to set a secret key. For security reasons, this should be unguessable. Set it like this (globally in your app).
from flask import (Flask, render_template, make_response,
request, redirect, url_for, flash)
app = Flask(__name__)
app.secret_key = 'replace this string'
In the all_examples.py
file, we generate a random secret key each time.
Example of Flashing¶
The example_flashing.py
file demonstrates flashing.
Here are the Flask routes:
from flask import (Flask, url_for, render_template, request,
redirect, flash)
import math
from all_examples import app
@app.route('/log2/', methods=['GET','POST'])
def log2():
if request.method == 'GET':
# this gets the blank form by visiting the URL
return render_template('log2.html')
else:
if request.form.get('num') is None:
flash('No "num" in the request')
return render_template('log2.html')
num = request.form.get('num')
try:
x = float(num)
except:
flash('Could not convert {} to float'.format(num))
return render_template('log2.html')
if x == 0:
flash('Definitely cannot compute log of zero.')
return render_template('log2.html')
if x < 0:
flash('''Cannot compute log of negative;
using absolute value instead''')
x = abs(x)
# finally!
y = math.log(x,2.0)
return render_template('log2.html',x=x,y=y)
Breakout Exercise: People Born in Month¶
Your flask3
folder has a subfolder pa1
which has a copy of the
people_app
with a few extras in it.
Your goal is to add a page with a form that allows the user to request a list by the birthmonth they specify:
You'll also process that form input and respond with the correct list
Part 1 the Form¶
- add a route that sends the empty form to the user. This only needs to be a simple route that renders the template and returns it.
- test that; you'll have to type in the URL
- add a hyperlink to the main page that goes to the correct URL
- test that
Part 2 Processing the Form¶
- add a route that will process the submitted form.
It's just vacous for now. Return a boring page, like
hello_world
- modify the form to send the data to that route
- modify the route to print the submitted form input to the console.
You'll have to import the
request
object. - test that
- convert the value to an int
- Replace the vacuous response with a real response. You can copy/paste the code from earlier in the file (though copy/paste is usually a bad sign). We'll fix this soon.
- test that
Part 3 Redirect¶
- Instead of the copy/pasted code, return a
redirect
to the correct route. You'll have to importredirect
. - test that
Part 4 Flashing¶
- Import
flash
from Flask - set the
secret_key
attribute of theapp
object. - flash the same message as you printed
- add the flashed messages incantation to your templates. See below
- test this
The flash incantation is something like this. You can modify the HTML, such as using OL and LI instead of DIV and P.
{% with messages = get_flashed_messages() %}
{% if messages %}
<div id="messages">
{% for msg in messages %}
<p>{{msg}}</p>
{% endfor %}
</div>
{% endif %}
{% endwith %}
Solution¶
The solution is in the pa-done
folder. Look at the templates and
compare the done.py
file with the app.py
file.
Summary¶
You know about
- forms
- request methods
- redirects
- flashing