Lesson 2

Installation, Basic Application Structure & Templates

Share Tweet

Contents

  1. Resources
  2. Introduction
  3. Installation
  4. Basic Application Structure
    1. Initializing an Application Instance
    2. Routes and View Functions
    3. Development Web Server
    4. The Request-Response Cycle
    5. Flask Extensions
  5. Templates
    1. The Jinja Template Engine
    2. Bootstrap Integration with Bootstrap-Flask
    3. Custom Error Pages
    4. Links
    5. Static Files
    6. Flask-Moment
  6. Exercises
  7. Further Reading

Resources

  • Contents of this lesson are referred from "Miguel Grinberg. Flask Web Development, Second Edition. O'Reilly Media, Inc., 2018.". Some up-to-date modifications of this textbook will be presented on this page.
    • Chapter 1 - Installation
    • Chapter 2 - Basic Application Structure
    • Chapter 3 - Templates
    • Sample Code of Lesson 2
  • Python IDE:

Introduction

Flask is a popular micro-framework that its aim is to keep the core services simple but extensible. By default, Flask does only include basic core services in order to make sure that any projects based on Flask can be easily implemented, integrated and deployed. Thus, Flask supports extensions to add functionalities to your application. For example, Jinja is a fast, expressive, extensible templating engine that allows manipulating data between front-end and back-end. There are a number of handy extensions that provide database integration, form validation, upload handling, various open authentication technologies, and more.

Flask has three main components:

  • The routing, debugging, and Web Server Gateway Interface (WSGI) — Flask uses the Werkzeug routing system which was designed to automatically order routes by complexity. This means that you can declare routes in arbitrary order and they will still work as expected. This is a requirement if you want to properly implement decorator based routing since decorators could be fired in undefined order when the application is split into multiple modules. Another design decision with the Werkzeug routing system is that routes in Werkzeug try to ensure that URLs are unique. Werkzeug will go quite far with that in that it will automatically redirect to a canonical URL if a route is ambiguous.
  • Template engine — Flask uses Jinja2 as a default template engine. Jinja2 has an extensive filter system, a certain way to do template inheritance, support for reusable blocks (macros) that can be used from inside templates and also from Python code, supports iterative template rendering, configurable syntax and more.
  • According to [2], Flask also has another component that enables the command-line integration that comes from Click. This component allows Flask to be able to execute scripts to access built-in, extension, and application-defined commands. For example, the --help command gives more information about any commands and options; or the run command will start the development server.


Installation

  • Notes:
    • The sample code presented in this chapter and the others have been verified to work with Python 3.5 and 3.6.
    • If you plan to use Microsoft Windows operating system, you should consider to use Windows Subsystem for Linux (WSL) or Cygwin. If you are using MacOS or Unix-based OS, you can ignore this notice.
    • The terminal commands presented in this lesson and the others are mainly based on MacOS (Version 10.15.7). For commands on Microsoft Windows operating system, you can refer to the textbook presented in the Resources section.
  • Create a Virtual Environment with Python 3:
    • Install virtual environment which allows you to manage separate package installations for different projects, they essentially allow you to create a virtual isolated Python installation and install packages into that virtual installation. We recommend the following virtual environments tools: virtualenvwrapper, virtualenv, venv. If you are using MacOS 10.15 and later, you can refer this tutorial to install virtualenvwrapper.
    • You can now create a virtual environment to install necessary libraries. For example, if you decide to use virtualenvwrapper, you should get familiar with the following commands:
      • workon will print a list of environments, or empty.
      • mkvirtualenv myflask — A new environment, myflask, is created and activated.
      • This time, the workon myflask will activate the myflask environment.
  • Installing Flask:
    • To install Flask into the virtual environment, make sure the desired virtual environment is activated, and then execute the command: $ pip install flask
    • When you execute this command, pip will not only install Flask, but also all of its dependencies. The results after executing this command is as Fig. 1.
    • Fig 1. Installing Flask.
      Fig 1. Installing Flask.
    • You can also verify that Flask was correctly installed by starting the Python interpreter and trying to import it (as shown in Fig. 2):
      • $ python
      • >>> import flask
      • >>> flask.__version__
      • '2.0.2'
      Fig 2. Validating Flask installation.
      Fig 2. Validating Flask installation.


Basic Application Structure

After completing this section, you will learn about the different parts of a Flask application, and also write and run your first Flask web application. We organize this lesson as follows:

  1. Initializing an Application Instance
  2. Routes and View Functions
  3. Development Web Server
  4. The Request-Response Cycle
  5. Flask Extensions

Initializing an Application Instance

Application instance is a Flask object that handles all requests passed from the web server using Web Server Gateway Interface (WSGI) protocol. Fig. 1 shows how to initialize Flask application object:

from flask import Flask
app = Flask(__name__)

Routes and View Functions

Clients such as web browsers send requests to the web server, which in turn sends them to the Flask application instance. The Flask application instance needs to know what code it needs to run for each URL requested, so it keeps a mapping of URLs to Python functions. The association between a URL and the function that handles it is called a route.

Flask provides the app.route() decorator to define a route in a Flask application. The app.route() decorator supports both static and dynamic routes. Dynamic routes allow you to add custom params to your URLs, for example, the URL for a profile page often has the format http://example.com/user/<user-name> where <user-name> is a parameter that makes it different for each user. The portion of the route URL enclosed in angle brackets is the dynamic part. Any URLs that match the static portions will be mapped to this route, and when the view function is invoked, the dynamic component will be passed as an argument. The dynamic components in routes are strings by default but can also be of different types. For example, the route /user/<int:id> would match only URLs that have an integer in the id dynamic segment, such as /user/123. Flask supports the types string, int, float, and path for routes. To get familiar with app.route() decorator, you first create a Python file named hello.py with the contents as shown in Fig. 3.

Fig 3. Examples of app.route() decorator.
Fig 3. hello.py: Examples of app.route() decorator.

The index() function as shown in Fig. 3 is a sort of static route. This function is a special case since it is also a root of your application. The user() function is a sort of dynamic route function where the portion of the route URL enclosed in angle brackets is the dynamic part, i.e., <name> in this case.

Development Web Server

Flask applications include a development web server that can be started with the flask run command, as shown in below figure. Before executing this command, you need to specify the application instance in the FLASK_APP environment variable, you can also enable debug mode by setting a FLASK_DEBUG=1 environment variable.

  • $ workon <environment_name>
  • $ export FLASK_APP=hello.py
  • $ export FLASK_DEBUG=1
  • $ flask run
Fig 4. Console window of Flask's development web server.
Fig 4. Console window of Flask's development web server.
The Request-Response Cycle

This section is available in the [2] textbook, we thus recommend that you read this section carefully to understand how Flask handles the request-and-response cycle.

Flask Extensions

As we mentioned earlier, Flask is an open framework that allows you to use any extensions that fit your needs. For example, you can use the Bootstrap-Flask extension to make your web-app to be responsive on any platform such as mobile, tablet, desktop. Or using Jinja extension to render dynamic HTML pages in the way that the business logic and presentation logic are separated to improve the maintainability of the application. In the next section, we will present how to work with Jinja in Flask.


Templates

(Extracted from "Miguel Grinberg. Flask Web Development, Second Edition. O'Reilly Media, Inc., 2018.")

The key to writing applications that are easy to maintain is to write clean and well-structured code. The examples that you have seen so far are too simple to demonstrate this, but Flask view functions have two completely independent purposes disguised as one, which creates a problem.

For example, consider a user who is registering a new account on a website. The user types an email address and a password in a web form and clicks on the Submit button. On the server side, a request with the data provided by the user arrives, and Flask dispatches it to the view function that handles registration requests. This view function needs to talk to the database to get the new user added, and then generate a response to send back to the browser that includes a success or failure message. These two types of tasks are formally called business logic and presentation logic, respectively.

Mixing business and presentation logic leads to code that is hard to understand and maintain. Imagine having to build the HTML code for a large table by concatenating data obtained from the database with the necessary HTML string literals. Moving the presentation logic into templates helps improve the maintainability of the application.

A template is a file that contains the text of a response, with placeholder variables for the dynamic parts that will be known only in the context of a request. The process that replaces the variables with actual values and returns a final response string is called rendering. For the task of rendering templates, Flask uses a powerful template engine called Jinja.

This section will cover the following topics:

  1. The Jinja Template Engine
  2. Bootstrap Integration with Bootstrap-Flask
  3. Custom Error Pages
  4. Links
  5. Static Files
  6. Localization of Dates and Times with Flask-Moment


The Jinja Template Engine

Putting the HTML render codes directly in Python functions is not a good practice, as shown in Fig. 3. Mixing business and presentation logic leads to code that is hard to understand and maintain. Moving the presentation logic into templates helps improve the maintainability of the application. To getting started with Jinja, we create index.html and user.html files and place them in the templates/ directory, as shown in below figure.

Fig 5. Example of using Jinja templates.
Fig 5. Example of using Jinja templates.

By default Flask looks for templates in the /templates directory located inside the main application directory. The hello.py from previous example is now modified in order to take advantage of the render_template() function to render dynamic contents on a pre-defined template. The function render_template() provided by Flask integrates the Jinja template engine with the application. This function takes the filename of the template as its first argument. Any additional arguments are key-value pairs that represent actual values for variables referenced in the template, as shown in Fig. 6.

Fig 6. Rendering a template with dynamic contents.
Fig 6. hello.py: Rendering a template with dynamic contents.
Bootstrap Integration with Bootstrap-Flask

Bootstrap is an open-source web browser framework that helps create clean, attractive, and responsive web pages that are compatible with modern web browsers such as mobile, tablet, desktop. Bootstrap is a client-side framework, thus the server just provides the reference to the CSS and JavaScript files of Bootstrap.

Notice — We can take advantage of Bootstrap-Flask extension which allows you to define a base template and then extend the base template to create new ones. By using Bootstrap-Flask extension, it makes the integration task much easier and simpler, and still keeps the changes nicely organized. We note that the Bootstrap-Flask is different from the out-dated Flask-Bootstrap which is presented in the textbook. Flask-Bootstrap has been obsolete for years, because it only supports Bootstrap 3. Therefore, we will use Bootstrap-Flask instead which supports the current up-to-date versions of Bootstrap. We also note that some code snippets based on Flask-Bootstrap in the textbook may not be compatible with the Bootstrap-Flask extension.

Bootstrap-Flask can be installed via Pip by executing the following commands (in your current Python environment):

$ pip install bootstrap-flask

You should note that Bootstrap-Flask can not work with Flask-Bootstrap at the same time. If you have already installed Flask-Bootstrap in the same Python environment, you have to uninstall it and then reinstall this project:

$ pip uninstall flask-bootstrap bootstrap-flask
$ pip install bootstrap-flask

To make the Bootstrap-Flask available in your application, you need to initialize it by importing the flask_bootstrap and defining its instance, as shown in Fig. 7.

Fig 7. hello.py: Initializing Bootstrap-Flask
Fig 7. hello.py: Initializing Bootstrap-Flask

For reasons of flexibility, Bootstrap-Flask does not include built-in base templates. Thus, you need to define a base template by yourself. Be sure to use an HTML5 doctype and include a viewport meta tag for proper responsive behaviors, as shown in below figure.

Fig 8. templates/base.htm: Define a base template
Fig 8. templates/base.html: Define a base template

With the defined base template, we can now modify the user.html template to match the new designed template. The user.html template defines two blocks called title and page_content. These blocks are based on the base template. If you do not override the content of the title block, it will take the value defined in the base template; otherwise, it will take the new value that you override. The page_content includes both static and dynamic information that you need to render on the web page. Fig. 9 presents the updated version of the user.html file.

Fig 9. user.html: Modify the User template
Fig 9. user.html: Modify the User template
Custom Error Pages

The two most common HTTP response status codes are 404 and 500 where 404 means that the server can not find the requested resource and 500 means that the server has encountered a situation it does not know how to handle. We can take advantage of Flask's error handler to define custom error pages. As shown Fig. 10, we first define two new functions to handle the two HTTP error codes (404 and 500), i.e., page_not_found(e) and internal_server_error(e). You should notice that we use the @app.errorhandler() decorator with the parameter is the corresponding error code. With these two new back-end functions, we now need to define their corresponding templates (front-end) to render the contents. In particular, we create templates/404.html and templates/500.html, and then change the title and page_content blocks to appropriate contents, as shown in Fig. 11 and 12.

Fig 10. hello.py: Implement custom error pages
Fig 10. hello.py: Implement custom error pages
Fig 11. templates/404.html: Implement the template for 404 error code
Fig 11. templates/404.html: Implement the template for 404 error code
Fig 16. templates/500.html: Implement the template for 500 error code
Fig 11. templates/500.html: Implement the template for 500 error code

Since the demo application is simple, we can insert direct links to each HTML <a> tag. However, an application that has dynamic routes with variable portions are more complicated to insert static/direct URLs to the templates. The main issue will appear when you decide to modify a current route, you thus need to traverse all the source code to find the current URL and update to the new one. To avoid this issue, Flask provides the url_for() helper function which generally generates URLs from the information stored in the application's URL map.

The url_for() function must have at least one parameter which is the name of the corresponding function that has app_route() decorator. For example, in the current hello.py, the url_for('index') will return / which is the root URL of the application. The url_for() function also has dynamic parts as keyword arguments. For example, url_for('user', name='kelvin', _external=True) would return http://localhost:5000/user/kelvin.

Keyword arguments sent to url_for() are not limited to arguments used by dynamic routes. The function will add any arguments that are not dynamic to the query string. For example, url_for('user', name='john', page=2, version=1) will return /user/john?page=2&version=1. The _external=True argument in the url_for() will indicate the function to return an absolute URL such as http://localhost:5000/ when working on a development web server.

Static Files

Along with the Python code, Flask also supports static files such as images, JavaScript, CSS that are referenced from the HTML source code. To enable static files mode on Flask, you first create a directory named "static" in the root directory of your application which is a special route supported by Flask. For example, a call to url_for('static', filename='css/styles.css', _external=True) would return http://localhost:5000/static/css/styles.css.

To demonstrate the use of static files, we modify the base template (base.html) to define the favicon which is a small image displayed next to the page title in the browser tab. To properly render the favicon, you have to create a directory named "static" (at root level) and place this file inside the directory.

Fig 17. base.html: Declare the static link to the favicon file via url_for() function
Fig 17. base.html: Declare the static link to the favicon file via url_for() function
Flask-Moment

When your application is available on the Internet and welcomes users to use the application, there is a small issue related to the localization of dates and times. On the server side, it needs to maintain a uniform time-and-date value which is independent from the users' location in order to ensure consistency. The Coordinated Universal Time (UTC) is often used to save time-and-date information. On the client side, we need to present the time-and-date based on users' region. By doing so, you can enhance the user experience (UX) with your application.

We can take advantage of the Moment.js library which is a popular JavaScript library that renders dates and times in the browser. Flask-Moment is an extension of Flask that makes the integration of Moment.js to your application become easy. Flask-Moment can be installed via pip as follows:

$ pip install flask-moment

After installing Flask-Moment, you need to initialize in a similar way to Bootstrap-Flask, as shown in below figure.

Fig 18. hello.py: Initialize Flask-Moment
Fig 18. hello.py: Initialize Flask-Moment

Flask-Moment depends on the jQuery.js in addition to Moment.js. Since Bootstrap already includes the jQuery.js, only Moment.js needs to be added to the base template to make Moment.js available to all extended ones, or added to the templates that require Moment.js. Fig. 19 shows how to insert the Moment.js to the base template. We note that the additional JavaScripts are often placed at the bottom of web pages before the </body> tag. Since the HTML code is rendered prior to loading the scripts, it will make the page load faster.

Fig 19. base.html: Insert Moment.js to the base template
Fig 19. base.html: Insert Moment.js to the base template

In Fig. 20, 21 and 22, we demonstrate the use of Flask-Moment. First, we pass the datetime.utcnow() value to the template via the current_time variable. Then, on the template, we use the Moment.js to render the timestamp in two formats, the first one is in LLL format which will result as "Jun 15, 2022 1:50 PM", and the second one is in relative time format such as "14 hours ago" by using the fromNow() function.

Fig 20. hello.py: Demonstrate the use of Flask-Moment by passing the date-and-time values to a template
Fig 20. hello.py: Demonstrate the use of Flask-Moment by passing the date-and-time values to a template
Fig 21. index.html: Render the time-and-date value on a web page with Moment.js
Fig 21. index.html: Render the time-and-date value on a web page with Moment.js
Fig 22. The results when rendering the time-and-date value on a web page with Moment.js
Fig 22. The results when rendering the time-and-date value on a web page with Moment.js

Besides rendering time-and-date values in many formats, Moment.js can render time-and-date to many languages by passing the two-letter language code to function locale(), right after the Moment.js library is included in the base template. You should refer to the Moment.js documentation to know more ways to manipulate time-and-date values.


Exercises

  1. As presented in Debug Mode section in Chapter 2, we can enable debug mode by setting the FLASK_DEBUG=1 environment variable before invoking flask run command. In this exercise, you will get familiar with debug mode by implementing some functions that return errors, as shown in below figure.
  2. Fig 23. Flask debugger.
    Fig 23. Flask debugger.
  3. Define a get_user(email) function with @app.route('/get-user/<email>') decorator where <email> is given by a client. The function first need to validate whether an input (email) is in a right format or not by using Regular Expressions (Regex). You can use this tool to learn, build, and test regular expressions. If an input is valid, the get_user(email) function will look up a user based on the given email, and return a string such as Hello [FIRST_NAME] [LAST_NAME] where [FIRST_NAME] and [LAST_NAME] are pre-defined. Otherwise, the funtion will return status code 404.
  4. Flask context globals have 4 variables, i.e., current_app, g, request, session where the request object encapsulates the contents of an HTTP request sent by the client. You are required to implement a function that return User-Agent information of a client, as shown in below figure.
  5. Fig 24. Obtain User-Agent information of a client.
    Fig 24. Obtain User-Agent information of a client.
  6. Moment.js is a JavaScript library that parses, validates, manipulates, and displays dates and times in the browser. Flask-Moment is an extension for Flask applications that makes the integration of Moment.js into Jinja templates. In this exercise, you will render the time-and-date values based on users' regions. In particular, you need to create a new dynamic route such as /get-date-time/<region-code> where <region-code> can be the two-letter language code; and then, parsing the region information to render the appropriate time-and-date values.


Further Reading

  1. Python — Object Oriented Programming
  2. Debugging Application Errors — Flask Documentation (2.1.x)
  3. Tutorial — Flask Documentation (2.0.x)
  4. Flask-RESTful

Relaxing 🧘