Introduction to Flask

Flask is a Python framework for creating lightweight web sites and API endpoints. This post is about my first experience using Flask.

Having dabbled a little with Python libraries like django and web2py, I was attracted to Flask by its simplicity and minimilism. Here’s how I started a simple project.

This is the first in a series of posts as I learn about Flask. I’m going to build a simple application to take secret estimates from an Agile team, aggregating the results and showing on screen. Today is just about getting a feel for what Flask can do.

This is my first attempt to use Flask; I’ll be discovering how to use the framework as I go. There may be mistakes and I may have to back track from poor early decisions. Feel free to send me advice or questions at ronan@failedtofunction.com.

For more information on flask, visit their website: http://flask.pocoo.org/

Initial Setup

Fist install Flask into your virtualenv (if you are not familiar with this, it is a tool that allows you to create isolated environments. See https://virtualenv.pypa.io/en/stable/ for details):

pip install flask

Here’s all the code you really need to set up a site that responds to two URLs:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello, World!'

@app.route('/about')
def about():
    return "About this site."

I’ve placed the above code in a file call controllers.py in a module called estimator. The only other file in the module for now is an empty __init__.py which is where I will move the initialisation and other configuration later. For now it is sufficient to place it all in the controller.

In the root of the project I’ve used a file run.py to act as the entry point for the application as follows:

from estimator.controllers import app
if __name__ == '__main__':
    app.run()

Run by simply calling run.py. You can also turn on development mode, which allows changes made to the code to be automatically loaded. The application will run on port 5000 by default. Note that my virtualenv is using Python 3.

$ FLASK_ENV=development python run.py
 * Serving Flask app "estimator.controllers" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

What about testing? I’ve decided to use Pytest over some of the other notable testing frameworks. My choice is based on the simplicity of Pytest, especially not having to use a subclass of the testing framework.

The tests are placed in a tests/ folder. I’ve defined a single test as follows in a file called test_controller.py:

from estimator.controllers import app

client = app.test_client()
client.testing = True

def test_index():
   response = client.get('/')
   assert response.status == '200 OK'
   assert response.get_data(as_text = True) == 'Hello, World!'

def test_about():
   response = client.get('/about')
   assert response.status == '200 OK'
   assert response.get_data(as_text = True) == 'About this site.'

def test_page_not_found():
   response = client.get('/not_there')
   assert response.status == '404 NOT FOUND'

Basic enough so far, but it represents a simple, fully tested, deployable application. As I extend the application, at least I can use test driven development.

Note that to get the tests to run, I had to include an empty __init__py file in the tests/ module. Running the tests is simple: just type pytest at the root of the project and it finds all the tests automatically, generating a nice report.

Templates

Rather than return simple text to the screen we can make use of templates. Lets swap the Hello, World page for a template showing real html.

The first step is to change the test, making sure that it fails. Something like this should be sufficient:

def test_index():
    response = client.get('/')
    assert response.status == '200 OK'
    html = response.get_data(as_text = True) 
    assert '<title>Agile Anonymous Estimation</title>' in html
    assert 'Let’s Estimate Something' in html

To use the template, first we need to change the controller to import render_template and then use this instead of returning text.

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def hello():
    return render_template('index.html')

Templates should be placed by default in a folder templates/ and they use jinja2 templating language. Here is a sample I placed in a file called index.html. It looks just like html at the moment.

<!doctype html>
<html lang="en">
<head>
    <title>Agile Anonymous Estimation</title>
</head>
<body>
    <h1>Let’s Estimate Something</h1>
</body>
</html>

Bootstrap

The above template was just raw html and does not take advantage of Jinja’s underlying abilities. Let’s add bootstrap to the mix. One way of doing this would be to download the bootstrap css files and place them in the static folder, allowing us to reference them from there, but there is an easier way. Flask provides this through an extention module, which we can add to the virutalenv with:

pip install flask-bootstrap

I’m going to add the following test to ensure that the html includes the loading of the bootstrap css file:

def test_index_contains_bootstrap():
    response = client.get('/')
    assert response.status == '200 OK'
    html = response.get_data(as_text = True) 
    assert 'bootstrap.min.css' in html

Bootstrap has to be loaded before it can be used. Let’s add an import and a load the object in controllers.py(lines 2 and 4 here are new):

from flask import Flask, render_template
from flask_bootstrap import Bootstrap
app = Flask(__name__)
bootstrap = Bootstrap(app)

We can change the template to make avoid repeating boilerplate html. Jinja encloses commands within blocks denote like this: {% command %}. The following code extends a bootstrap template, sets the title using the {% block title %} command and encloses the body within a {% block content %} commnad:

{% extends "bootstrap/base.html" %}
{% block title %}Agile Anonymous Estimation{% endblock %}
{% block content %}
    <div class="container">
        <h1>Let’s Estimate Something</h1>
    </div>
{% endblock %}

Here’s a screenshot showing what the page looks like with the source shown below. I'm loving how simple and concise the code is to produce this content.

That’s enough for now. I’m going to keep at this and will update with a further post in the near future. If you want to take a closer look at the code you can find it on github here: https://github.com/rkie/estimator