Flask 2
In this reading, we'll dig deeper into Flask, looking at issues like:
- constructing URLs
- extracting information from GET and POST requests
- sending static files to the browser
- rendering templates with Jinja2
Flask Tutorials¶
Please read the following:
Summary¶
The following information summarizes the above. I don't think it's a substitute, but it may supplement.
Constructing URLs¶
They don't motivate this well. Here's my attempt.
Constructing a URL doesn't seem hard, but nevertheless, Flask provides a
function to do so, called url_for
, which you should use instead of doing
it yourself. This is important to make your Flask code portable and
more easily deployed.
For example, when I developed the CS304 demo, I had urls like
/lookup
and /nm/123
. But when I deployed the demo, every URL got
prefixed with /cs304demo/
(by my choice). So I now had urls like
/cs304demo/lookup
and /cs304demo/nm/123
. And I didn't have to
re-write any of my code! All the urls were re-built by url_for
to
use the prefix. That would not have happened if I had just specified
the URL.
always use
url_for
The argument to url_for
is not the URL but rather the name of
the handler function as a string. (In practice, these are often the
same, but not always.) For example:
@app.route('/')
def home():
return render_template('home.html')
I would generate the url for that page by saying url_for('home')
Note that you should use url_for
anywhere that a URL occurs,
including in your template files.
(If you need to generate url in a static file, see this Stack Overflow post on Flask url_for in a CSS file.)
HTTP Methods¶
The default way that a request comes in is as a GET request. Sometimes, it comes in as POST.
Flask provides a "global" variable (it's not really a global, but we
won't worry about the distinction) called request
that has a lot of
information about the request. One attribute is the method
.
When you define a route, you can specify which methods it should respond to. The default is just GET, but you can add POST or make it respond solely to POST. Examples:
# this route supports only GET
@app.route('/aaa/')
def aaa():
return '<p>request arrived as GET'
# this route supports both, so we have to use an IF
@app.route('/bbb/', methods=['GET','POST'])
def bbb():
if request.method == 'GET':
return '<p>request arrived as GET'
else:
return '<p>request arrived as POST'
# this route supports only POST
@app.route('/ccc/', methods=['POST'])
def ccc():
return '<p>request arrived as POST'
Form Data¶
An HTML form can send data to the server. Suppose that the form has inputs: city, state, zip and the user submits Wellesley, MA, 02481
If the FORM uses the GET method, the data comes in the URL:
/path/to/place&city=Wellesley&state=MA&zip=02481
If the FORM uses the POST method, the data comes in the body, similarly encoded:
/path/to/place
other headers
city=Wellesley&state=MA&zip=02481
Accessing Form Data¶
Flask can access the data either way it's sent, but in different places.
- Flask puts GET data in request.args
- Flask puts POST data in request.form
Both objects are dictionary-like objects, and can be accessed the same way normal Python dictionaries are accessed.
This route would reflect back the form data:
@app.route('/reflect/', methods=['GET','POST'])
def reflect():
if request.method == 'GET':
city = request.args['city']
state = request.args['state']
zip = request.args['zip']
return 'You sent, via GET, {}, {}, and {}'.format(city,state,zip)
else:
city = request.form['city']
state = request.form['state']
zip = request.form['zip']
return 'You sent, via POST, {}, {}, and {}'.format(city,state,zip)
Static Files¶
Web sites have a lot of static pages: CSS, JS, logo images, etc.
Flask can serve those out, just like Apache does.
The static files are put in a folder called static
that is a sibling of templates
URLs are generated with url_for
but with 'static' as the first
argument and a keyword argument of filename
for the rest. An
expression like this might be put in a template file:
url_for('static', filename='style.css')
Templates¶
We'll learn about templates in two parts. We'll do some simple templating, and deal with inheritance another day.
Jinja2¶
With Flask and Jinja2, all the templates in the templates
folder are
read into memory and parsed when the flask application loads. This is
great for efficiency. It does, however, mean that an error in any template
file, even one you're not using, can cause errors when your app
loads. Fair warning.
The Template Language¶
Now let's turn to the template language.
Variables¶
Double braces surround python variables, and the variable's value is inserted:
Look at the templates/hello.html file.
<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
</head>
<body>
<h1>Hello</h1>
<p>My name is {{name}}.</p>
</body>
</html
Here's the route that uses that template. Notice that we use the Flask
render_template
function to combine the static template file with
the random dynamic data (one of the four names).
@app.route('/hello/')
def hello():
nom = random.randomElt(['Inigo Montoya',
'Harry','Ron','Hermione'])
return render_template('hello.html',
name = nom)
Here's an example of a template file with a static CSS file, as well as a placeholder for a name:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CS304: Flask</title>
<link rel="stylesheet" type="text/css"
href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<h1>Hello</h1>
<p>My name is {{name}}.</p>
</body>
</html>
Dictionaries¶
You can also access elements of dictionaries:
@app.route('/tim/')
def tim():
art = {'name': 'Arthur, King of the Britons',
'quest': 'To seek the grail',
'color': 'blue'}
return render_template('tim.html', dic = art)
Look at the templates/tim.html file:
<!doctype html>
<html>
<head>
<title>Tim</title>
</head>
<body>
<p>Name: {{ dic.name }}.
<p>quest: {{ dic.quest }}.
<p>favorite color: {{ dic.color }}.
</body>
</html
Notice the shortcut syntax (like JavaScript's): you can say dic.key
instead of dic['key']
.
Arrays¶
If you have an list of data, you can loop over it. Here's a template that has a loop that uses Python syntax:
@app.route('/things/')
def things():
favs = ['Raindrops on roses',
'whiskers on kittens',
'bright copper kettles',
'warm woolen mittens',
'brown paper packages tied up with strings']
return render_template('things.html', things = favs)
Look at the templates/things.html file.
<!doctype html>
<html>
<head>
<title>Things</title>
</head>
<body>
<h1>Favorite Things</h1>
<ul>
{% for thing in things %}
<li>{{ thing }}</li>
{% endfor %}
</ul>
</body>
</html>
Those types: values, lists and dictionaries, will get us a long way.
The Jinja2 language also has conditionals as well as loops.