Flask 2
Templates and Rendering
# p12_html_response_without_templatesReturn full HTML documents complete with headings and hyperlinks in our Flask application Instead of running short HTMl snippets or redirecting users When run, visit http://127.0.0.1:5000/ ⇒ a complete / full HTML appears Ctrl-U: View Source to verify that the HTML for the page comes directly from the home() function of the Python programIf visit e.g. http://127.0.0.1:5000/greet/Siti/, will display “Hello, Siti” immediately! Generates dynamic HTML content ⇒ powerful but dangerous: greet_name() function inserted name variable into output without checking its content Malicious user may inject code into the output to cause mischief E.g. http://127.0.0.1:5000/greet/enter%20password:%20<input><h1>Thanks/ ⇒ appears to be a legitimate form asking for user’s password, but in reality, the form does not come from our application, and was injected into the page from HTMl code in the URL’s path ⇒ Can become messy to construct HTML documents by joining Python strings Generate HTML responses by manually manipulating strings in Python E.g. easy to confuse between use of HTML and Python in the home() and greet_name() functions import flaskapp = flask.Flask(__name__)@app.route('/')def home():html = '<!DOCTYPE html>\n<html>'html += ‘<head><title>Home Page</title></head>’ html += ‘<body><h1>Welcome to my home page!</h1>’ html += ‘<p>My favourite web site is ’ html += ‘<a href=“http://example.com/”>’ html += ‘example.com</a>.</p>’ html += ‘<p>You can greet Ryan ’ html += ‘<a href=“/greet/Ryan/“>here</a>.</p>’ html += ’</body></html>‘ return html@app.route('/greet/<name>/')def greet(name):html = '<!DOCTYPE html>\n<html>'html += ‘<head><title>Greetings!</title></head>’ html += ‘<body><h1>Hello, {}!</h1>‘.format(name) return htmlif __name__ == '__main__':app.run() |
|---|
- Put HTML content in a separate file (template) with placeholders for where the dynamic content should be inserted
- Whenever need to output HTML: Use a template engine to load the template & fill in the placeholders ⇒ also helps escape special characters (e.g. < >) when filling in placeholders, to avoid HTML injections
- Rendering = process of filling in placeholders of a template to produce the final HTML that is used for response
- Jinja2 Template engine
- Built-in template engine provided by Flask
- Default: All templates expected to be located in a subfolder named templates
- Create a subfolder templates in the folder where your Flask programs are stored, then save the file in this folder as home.html
# Template 1: templates/home.html<!DOCTYPE html><html><head><title>Home Page</title></head><body><h1>Welcome to my home page!</h1><p>My favourite website is<a href="http://example.com/">example.com</a>.</p><p>You can greet Alex<a href="{{ url_for('greet', name='Alex') }}">here</a>.</p></body></html> |
|---|
# Template 2: templates/greet.html<!DOCTYPE html><html><head><title>Greetings!</title></head><body><h1>Hello, {{ visitor }}!</h1></body></html> |
|---|
- Placeholders are surrounded by double braces ⇒ {{ Jinja2 expression }}
- When using templates, don’t confuse Jinja2 with Python expressions ⇒ have diff. syntaxes & operate in diff. environments
- E.g. len() not available in Jinja2 expressions
- Rendering of a template is typically performed by a function render-template()
# p13_html_response_with_templates.pyRun program and visit http://127.0.0.1:5000/ View Source (Ctrl-U) to verify the template is rendered and no placeholders are present in its HTML source (verify by visiting http://127.0.0.1:5000/greet/Alex/) 1 import flaskfrom flask import render_templateapp = flask.Flask(__name__)@app.route('/')def home():return render_template('home.html') # renders a template named greet.html@app.route('/greet/<name>/')def greet(name):return render_template('greet.html', visitor=name)# specifies that within the template, value of name should be assigned to a <br> Jinja2 variable named visitorif __name__ == '__main__':app.run() |
|---|

- Parsing values to templates
- render_template() function accepts name of a template in the templates subfolder as its 1st argument, then 0 or more keyword arguments assigning values to Jinja2 variables that may be used by the template

- E.g. Render a template ‘greet.html’, within the template, value of named should be assigned to a Jinja2 variable ‘visitor’ ⇒ Jinja2 placeholder is replaced by this value
- Jinja2 variables != Python variables
- If render_template() is called without any keyword arguments, values from the Python environment are NOT passed to the template
- To use Python values in the generated HTML, must pass them over as keyword arguments when render_template() is called
- How render_template() retrieves a template file & replaces its placeholders to produce the final HTMl that is sent to the browser:

- Avoid hardcoded links
- render_template() automatically includes Flask’s url_for() function so paths can be generated based on the Python that needs to be called instead of being hardcoded ⇒ useful for creating HTML links
<p>You can greet Alex<a href="{{ url_for('greet', name='Alex') }}">here</a>.</p>⇒ more future-proof ⇒ can update paths without changing every template that links to it |
|---|
vs hardcoding of path:<p>You can greet Alex <a href="/greet/Alex/">here</a>⇒ shorter but need to update every time we change the path that is routed with a function |
- The safe filter
- Prevents HTML injection
- E.g. when type http://127.0.0.1:5000/greet/enter%20password:%20<input><h1>Thanks/ now:

- Ctrl-U to view source: < and > signs have been escaped with the appropriate HTML entities
- Jinja2 automatically performs this escaping so user input can be used in Jinja2 expressions freely without any safety concerns
- To apply safe filter to an expression: follow the expression with “|” and the name of the filter
- E.g.
# Template 3: templates/custom.html<!DOCTYPE html><html><head><title>Custom HTML</title></head><body><h1>Custom HTML</h1>{{ my_html|safe }}</body></html> |
|---|
# p14_raw_html_with_safe_filter.pyimport flaskfrom flask import render_templateapp = flask.Flask(__name__)@app.route('/')def home():return render_template('custom.html',my_html='<h1>This is my HTML!</h1>')if __name__ == "__main__":app.run() |
![]() String passed to template as my_html is rendered as raw HTML |
If remove safe filter: special characters < and > are escaped![]() |
- The length filter
- Since len() function not available in Jinja2 expressions
- To output length of string or list from a template
- Alt: perform calculation in Python & pass the value over to the template as a separate Jinja2 variable
# Template 4: templates/length.htmlVisit e.g. http://127.0.0.1:5000/ryan/ <!DOCTYPE html><html><head><title>Length of Name</title></head><body><h1>Length of Name</h1>Hello {{ name }}, your name is {{ name|length }} characters long! </body></html> |
|---|
# p15_name_with_length_filter.pyimport flaskfrom flask import render_templateapp = flask.Flask(__name__)@app.route('/<name>/')def length_of_name(name):return render_template('length.html', name=name)if __name__ == "__main__":app.run() |
- Jinja2 statements
- In typical Flask applications, most data processing and computation is done using Python code. Outputs from this computation are then passed to Jinja2 as numbers, strings, lists, and other plain data objects. The data is used to fill in a template to produce the final HTML
- Can also perform some using Jinja2, but not recommended to perform complex logic in a template
- But still useful to perform some simple logic in a template (e.g. selectively render parts of a template, or repeat a portion of the template for every item in a list)
- Jinja2 supports control flow ina template using Jinja2 statements (commands surrounded by {% and %}) that do not produce any output
- vs placeholders surrounded by {{ and }} that are usually replaced with output
- if statement
- Selectively include or exclude portions of the template
- Excluded portions of a template are simply not rendered
- Not grouped by indentation ⇒ need endif command to indicate where if statement ends
- if, elif, and else clauses are now demarcated by separate {% and %} blocks ⇒ no need colons
# Template 5: templates/results.html<!DOCTYPE html><html><head><title>Results</title></head><body><h1>Results</h1>{% if greet %}<p>Hello, {{ name }}.</p>{% endif %}{%if show_score %}<p>Your score is {{ score }}%.</p>{% elif score >= 50 %}<p>You passed.</p>{% else %}<p>You failed.</p>{% endif %}</body></html> |
|---|
# p16_results_using_if.pyimport flaskfrom flask import render_templateapp = flask.Flask(__name__)@app.route('/')def home():return render_template('results.html', greet=True,name='Alex', show_score=False, score=72)if __name__ == '__main__':app.run() |
- for-in statement
- Repeats the rendering of a portion of the template for every item in a list, tuple, string, or dictionary
- Jinja2 is not grouped by indentation ⇒ need endfor command
# Template 6: templates/table.html<!DOCTYPE html><html><head><title>Table of Results</title></head><body><h1>Table of Results</h1><table><tr><th>Subject Name</th></tr>{% for subject in results %}<tr><td>{{ subject }}</td><td>{{ results[subject] }}</td></tr>{% endfor %}</table></body></html> |
|---|
# p17_table_using_for.pyHardcoded dictionary mapping subject names to scores, passed to template for rendering ⇒ real world: likely retrieve names and scores from SQLite database import flask ![]() from flask import render_templateapp = flask.Flask(__name__)@app.route('/')def home():results = {‘English’: 75, ‘Mother Tongue’: 73, ‘Maths’: 76, ‘Computing’: 78 }return render_template('table.html', results=results)if __name__ == "__main__":app.run() |
Static Files
- Requested paths are treated as strings for route matching by Flask and do not usually refer to the location of real files or folders stored on server
- Lets us return a dynamically-generated response using render_template() instead of returning a static file
- Most HTML documents typically request for additional resources (e.g. style sheets, images) from additional URLs to display properly
- Unlike main content, these additional resources do not change often, so instead of running a Python function to generate the response, Flask lets us create a subfolder named static (in the same location as the templates folder) to store these resources ⇒ Flask then sets up a view named ‘static’ that (by default) routes any path starting with /static/ to the contents of this subfolder
# style sheet 1: static/styles.cssbody{background: yellow;}h1 {border-bottom: 1px solid red;color: red;} |
|---|
# Template 7: templates/stylish.htmlthe path of the static file within the static subfolder is passed to url_for() as a keyword argument named filename ⇒ not hardcode <!DOCTYPE html><html><head><title>Stylish Page</title><linkrel="stylesheet"href="{{ url_for('static', filename='styles.css') }}"><!--alt for above: <link rel="stylesheet" href="/static/styles.css">—> </head><body><h1>Stylish Page</h1><p>Look at how stylish this page is!</p></body></html> |
|---|
# p18_static_style_sheet.pyimport flaskfrom flask import render_templateapp = flask.Flask(__name__)@app.route('/')def home():return render_template('stylish.html')if __name__ == '__main__':app.run() ![]() Ctrl-U: style sheet URL placeholder is replaced with a path starting with /static/ |
Note: can run Flask in debug mode
- app.run(debug=True)
- ⇒ Flask reloads itself whenever it detects file changes so no need manual restarts
- But has several incompatibilities with IDLE (e.g. no output from print() and error msg)
Comments from the Word document
Footnotes
-
Comment by ANDREA TAN KAI XUAN HCI: how to check ↩



