flask
is a Python package which can help you to:
It is a lightweight web application framework which is popular, flexible, and easy to get started with.
from flask import Flask
app = Flask(__name__)
Flask
class definition, and assigned an instance of it to app
Things we don't need to worry about for now, but:
__name__
is a special Python variable which determines where Flask should look for files such as templates__main__
will be the value of __name__
if this file is itself being run directly rather than by another file@app.route('/')
/
def index():
return 'Hello, World'
index
is used for the root URLif __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
__name__
will equal __main__
because we are running the file directly (unimportant for now).run()
method of app
will serve the page at the given host
(required for replit)debug=True
means we can see changes in the page without running the code againTry changing the 'Hello, World' text and refreshing the page.
jinja
is a Python package which is used for templating. We can use it to:
The syntax used in Jinja templates is similar to Python.
This code snippet is taken from the example we'll see later, showing a part of the template where rows in a template are created based on data in a list of dictionaries:
{% for activity in activities %}
<tr>
<th scope="row">{{ loop.index }}</th>
<td>{{ activity['activity'] }}</td>
<td>{{ activity['price'] }}</td>
</tr>
{% endfor %}
{% ... %}
tags (here designating a for
loop){{ ... }}
tags (here looking up values in a dictionary)Rather than our page function to simply return text, we can use the flask.render_template()
function to:
This is a powerful combination which is central to making our web pages dynamic.
By default, Flask will look for templates in the /templates
directory next to the application code.
Here's a Flask example which uses Jinja templating, including the code we've just seen:
site_name = 'Bucket List'
activities = []
votes = [0, 0, 0, 0, 0]
for i in range(5):
resp = requests.get('https://www.boredapi.com/api/activity?type=recreational')
activities.append(resp.json())
site_name
to a string, which we'll use in our web pagerequests.get()
as seen previously to collect data from an APIactivities
now has a list of dictionaries containing data for our pagevotes
is a list of values which we can subsequently modifyrequest
¶flask.request
allows us to determine how a web page we've served with Flask was requested
requests
package we just used to fetch dataGET
and POST
requests@app.route('/', methods=['GET', 'POST'])
methods
parameter determines which types of request
for the given route
i.e. URL are allowed; by default this is only GET
def index():
if request.method == 'POST':
choice = int(request.form['choice'])
votes[choice] += 1
POST
request, we'll record the input by modifying our votes
listGET
request, this will be skipped and votes
left unmodifiedrender_template()
¶return render_template('index.html', site_name=site_name, activities=activities, votes=votes)
render_template()
function, giving our template file as the first argument, followed by two keyword arguments containing our data to be used by the template Take a look at base.html
:
<title>{{ site_name }}</title>
<body>
{% block content %}
{% endblock %}
</body>
site_name
will be used as the title
(shown in the browser tab)content
in the given location
_The rest of base.html
is simply a 'boilerplate' or 'blank canvas' for using the Bootstrap CSS framework:
Take a look at index.html
:
{% extends "base.html" %}
{% block content %}
...
{% endblock %}
the {% extends ... %}
statement tells Jinja to use base.html
as a starting point for this template
... placing what's between the {% block ... %}
and {% endblock ...%}
tags in the position with the same label (here content
)
there could be several such blocks within base.html
and index.html
, using different labels
{% for activity in activities %}
<tr>
<th scope="row">{{ loop.index }}</th>
<td>{{ activity['activity'] }}</td>
<td>{{ activity['price'] }}</td>
...
</tr>
{% endfor %}
{% for...%}
and {% endfor... %}
designate loops to be completed for each item in a listdictionary['key']
loop.index
returns the current iteration count; note that this isn't zero-indexed, and the first value in the table is 1
loop.index0
POST
requests¶<td>
<form action="." method="POST">
<button type="submit" class="btn btn-outline-primary" value="{{ loop.index0 }}" name="choice">
<span class="badge badge-light">{{ votes[loop.index0] }}</span>
</button>
</form>
</td>
form
with a method
attribute of POST
action
attributes tells the browser where to send the data, with .
meaning 'the current page'name
and value
attributes of the button
match with our logic in the index
function