Overview about the Flask web framework

This post is part of the series “How to build your own Network Configuration Generator”. You find the overview about the entire series here. The last state of the code is available at the Network Configuration Generator GitHub repository.

I’ll like to provide a quick overview about the Flask microframework and the associated modules and libraries that I used during the development of the Network Configuration Generator. There are many tutorials out there on “how to develop a Flask application”, therefore I will limit the code examples to a minimum in this post. I’ll focus on the resources that I’ve used for learning. As mentioned at the beginning of this series, I’m a totally Flask newbie. During the development of the Network Configuration Generator, I quickly recognized, that there are many other modules and libraries that are required to get a Flask based app working.

At the end of this post, I provide some insights how the Network Configuration Generator is structured and how I used Flask so far. I share a “short” script to get the current development version working on an Ubuntu Linux machine. At time of this writing, I’ll integrate the basic navigation and data model in the Flask application. The current development state is available on my Network Configuration Generator Github repository.

The Flask Microframework

The Flask microframework depends on two external libraries: the Jinja2 template engine and the Werkzeug WSGI toolkit. The out-of-the-box functionality that comes with the Flask library is a limited, but the entire library is build around the idea of extensibility. If you use only Flask, you’ll get the routing engine for the website and the template engine based on Jinja2. The following code example is from the homepage of the Flask microframework and provides the classic “Hello World” page within a Flask application:

from flask import Flask
app = Flask(__name__)

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

if __name__ == "__main__":
    app.run()

As you can see, there are only seven lines of code. To get the example working, you just need to install Flask and run the code example above:

$ pip3 install Flask
$ python3 hello.py
 * Running on http://localhost:5000/

As you see, the basic usage of Flask is quite easy and straightforward. If you like to learn more about it, just dive into the Flask Quickstart chapter in the documentation.

Object-Relational-Mapping using Flask-SQLAlchemy

Almost every application requires some kind of database. The Django Web Framework for example has its own Object-Relational-Mapping (ORM) functionality. In a nutshell: ORM provides an abstraction layer between the application and the underlying database, which means that you can work directly with the programming language to access the data and you don’t need to deal with SQL and the various dialects that are out there.

Flask doesn’t provide such a functionality out-of-the-box, but it is very common to use SQLAlchemy in this scenario. SQLAlchemy is a popular ORM mapper for the python programming language and is tightly integrated to Flask using the Flask-SQLAlchemy extension.

This is a large topic and I’ll like to provide just a resource that was very helpful for me to understand it: Flask by Example – Setting Up Postgres, SQLAlchemy, and Alembic. This post was part of a series on the Blog at RealPython, which covers a complete “Flask by Example” project.

The post discussed many topics, including the setup of SQLAlchemy and database migrations using Flask-Migrate, which is required when updating the model during the further development of the application. Within the Network Configuration Generator I used the most parts and pattern from this blog post, except the underlying database application. I prefer to use SQLite at time to reduce the external dependencies.

Interact with the User: Flask WT Forms (Flask-WTF)

By default, Flask has also no integrated Form handling. The gap is filled with the Flask-WTF module. It provides a set of classes to write a form in python that is rendered within the template to HTML. It also provides functions to prevent CSRF (cross site request forgery) attacks on your server. These types of attack involve a third-party that forges form data and send these to the server, which takes action accordingly.

The creation of a form (with CSRF protection) is quite easy. The following code example shows a simple form for a configuration template:

from flask_wtf import Form
from wtforms import StringField, TextAreaField

class ConfigTemplateForm(Form):
    name = StringField("name", validators=[DataRequired()])
    template_content = TextAreaField("template content")

Flask-WTF provides also an extension to create a Form based on a Model object using a special model_form function. At time of this writing, I only used this function within the Network Configuration Generator, for example the ProjectForm looks similar to the following code example:

ProjectForm = model_form(
    Project,
    base_class=Form,
    db_session=db.session,
    exclude_fk=True
)

Now you can work with the Form in your Flask view. Before accepting the form data on a URL, you need to add the methods parameter to the @app.route decorator. The following example shows, how the form data are processed:

@app.route(ROOT_URL + "project//edit", methods=["GET", "POST"])
def edit_project(project_id=None):
    """edit/add a new Project

    :param project_id:
    :return:
    """
    project = Project.query.get(project_id)
    form = ProjectForm(request.form, project)

    if form.validate_on_submit():
        try:
            form.populate_obj(project)
            db.session.add(project)
            db.session.commit()

            flash("Project %s successful saved" % project.name, "success")

            return redirect(url_for("view_project", project_id=project.id))

        except Exception:
            msg = "Project was not created (unknown error)"
            logger.error(msg, exc_info=True)
            flash(msg, "error")

    return render_template("project/edit_project.html", project=project, form=form)

This is a lightweight example on how to work with Forms in Flask. The further reading section provides some additional links to more detailed examples and advanced topics.

Automated Testing using the Flask-Testing Framework

“if it’s not tested it’s broken” – Bruce Eckel

The last part of this post contains one of the important things associated with programming: testing. I found a good article on the RealPython.com Blog about a “minimum viable test suite”. It was from my perspective a very good introduction to create test cases for a Flask Application using the Flask-Testing module, even if it focuses on a slightly different approach that I’ve used within the Network Configuration Generator.

Within the source code, you find a tests directory that contains all automated tests for this project. I usually create some unit-tests for the basic validation of the fundamental functions (e.g. Database constraints that I need to rely during the further development, some basic tests of the views and forms within the data structure to avoid some strange HTTP 500 errors etc.). A “second step” is the functional testing that verifies my Use Cases from a users perspective. I use the python bindings for the Selenium framework to emulate a user session, which clicks through a web browser session. This is the best verification for the Use Cases from my perspective.

Flask and the Network Configuration Generator

If you look at the Network Configuration Generator GitHub repository, you find the following directories and important files:

  • app – that contains the entire Flask application, templates and static files that are required for the web service
  • tests – that contains the basic test classes for Flask, the unit and functional test cases (based on the python unitest framework)
  • config.py – that defines all configuration settings, that are required for the Flask application
  • manage.py – that is used to create the database and the database migration files
  • run_server.py – python script that starts a local (development) server
  • requirements.txt – contains all python packages and libraries that the application depends on

Currently, you need the following steps to start the Network Configuration Generator on your local computer (assuming you have python3, git, pip3 and virtualenv installed):

1. Clone the Repository

$ git clone https://github.com/hoelsner/network-config-generator.git
$ cd network-config-generator

2. Create a new virtual environment for the Flask application

$ virtualenv -p /usr/bin/python3.4 venv
$ source venv/bin/activate

3. Install dependencies

(venv)$ pip3 install -r requirements.txt

4. Create database

At time of this writing, the database migrations were no part of the repository, therefore you need to create these using the Flask-Migrate commands.

$ python3 manage.py db init
$ python3 manage.py db migrate
$ python3 manage.py db upgrade

5. Start the development server

$ python3 run_local.py
2015-12-31 12:11:30,871 - werkzeug - INFO -  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

I will simplify this process at the end of this blog post series using Ansible and Vagrant, but this should be enough for today. Thank you for reading.


Further Reading