Tasks
This lab will take you through more advanced server-side concepts in Flask: templates, sessions, authentication and file upload (bonus).
A typical website has a common HTML design, with only portions of its code changing on a per-page basis with specific content. In order to prevent needless code duplication, a template engine is usually employed to obtain HTML documents from common layouts. A template is, basically, a HTML page interleaved with specific code blocks used to insert dynamically generated content from variables; many engines feature full programming languages that support loops and conditionals.
Flask readily integrates with the Jinja templating engine which uses Python-like statements to enrich a HTML page with programmatic content:
<!-- ... --> <body> <h1>My Webpage is {{ awesome_variable }}</h1> <ul id="main-menu"> {% for item in navigation %} <li><a href="{{ item.href }}">{{ item.caption }}</a></li> {% endfor %} </ul> {# a comment #} </body>
The Jinja templates usually reside inside the project's templates/
directory (check the Flask documentation if you want to change it) and can be rendered using the render_template
utility function.
HTTP is a stateless protocol, which means that any request (e.g., from a browser) must not assume that the server knows any of its historic actions.
To persist the state, servers often use the cookie feature to establish so-called sessions, simple key-value pairs which can either be stored inside the client's cache/memory (encrypted, of course) or on server-side (hence client will only store an unique identifier issued by the server, and the server will have a database entry for each client ID with its private session data).
When a session-based request arrives on the server, it reads the cookie, decodes the ID or retrieves the session data from its database and exposes session context variables to the application developers. In flask, the session
global var is uses exactly for this:
from flask import ..., session # ... @app.route("/") def account_page(): if "authenticated" not in session: return redirect("/login", code=302) # ...
Since the session data store is properly authenticated and encrypted, it is often used to provide a persistent authentication of the client (e.g., using the results from a login form), though server may also use its session to track the user's behavior (e.g., the pages it opened in the last minute, zones clicked etc.).
Sessions are often lost when either the server or the client browser is restarted (since they are stored in-memory or limited cache).
If a server wishes to store long-term persistent data, a database is the standard way to ensure data availability.
Databases are often fully-fledged applications in their own right and the web server communicates with them using various inter-process or socket-based channels (though client libraries are always provided), either using separate language (e.g., SQL) or specific/proprietary APIs (no-SQL).
But, for today, a simple disk-based file stored on the server will suffice (you should know how to do this in Python) ;)
Reminder to create your virtualenv and install Flask inside:
# either you or your IDE should create the Python Virtual Environment, e.g.: python3 -m venv .venv # activate it (note: you will need to do this every time you change your shell)! source .venv/bin/activate # finally, install libraries using pip python3 -m pip install flask
First, download the lab archive.
We now want to move the design from last time (a static html
file) to use Flask Jinja2-based template rendering.
For this, you must:
templates/_base.html
(base template) and templates/index.html
(child);_base.html
template should contain the common layout of all pages)!server.py
to call render_template
from router functions to actually serve our new design;second.html
and fill the Flask function to serve it (fill it with whatever content you want, like in the previous lab);Now, it's the time to add authentication to our website.
session
to store the user's state (e.g., an authenticated
bool + username
string keys).secret_key
for the session, otherwise it won't work!/login
and /logout
with the appropriate checks / actions.login.html
template page, or roll out your own!<form>
!authenticated
variable inside the Jinja template to conditionally display the user's status.session
is readily available inside Jinja2 templates!We want to store some of the user's personal data into a simple file-based “database” on the server.
account-details.html
template page for any TODOs there!value
HTML attribute on the <input>
tags to write them from Jinja templates;/account-details
route handler to read / write to the database.txt
file on the server;
Bonus task: add a upload image input inside the account-details
that uploads a file to the public/images/profile
subfolder (also: create it!):
type=“file”
attribute on the <input>
;multipart/form-data
encoding type attribute on the <form>
(Google it!);<img>
tag to display the profile photo on the user's account page when everything is done!