Flask application factories and blueprints

Patterns for scalable Flask applications

20 Nov '18

I wrote about application factories in 2016, but recently re-read the post in light of a HackerNews post, and… well, I think I can do better.

Application factories

Flask has documentation on application factories, and a tutorial. Even the default tutorial now uses an application factory.

The most straightforward way to create a Flask application is to create a global Flask instance […]. While this is simple and useful in some cases, it can cause some tricky issues as the project grows.

flask.pocoo.org

But why bother? It has to do with how Python loads modules. Let’s take a file creatively called file.py:

print("global")

def func():
    print("local")
Python 3.7.0
>>> from file import func
global
>>> func()
local

Oops. When I tried to import the function in an interactive session, the print function in the global context is executed. So if the Flask application is initialised outside of a function, it can’t be changed after importing the module (and before, really). If the Flask application is initialised inside a function, it can be customised!

This function creating the Flask application is called the application factory. (It’s an awful name, but naming stuff is hard. Factory methods are a thing in object-oriented programming, and this is kind of analogous.)

Simple, right? The tricky part is be figuring out how to hook Flask extensions up. We know this is bad, but this is easy to initialise:

# __init__.py
app = Flask(__name__)
# configuration of app here
# (have to use environmental variable or something?)

# models.py
db = SQLAlchemy(app)

But if app is created inside a function, how can db use it?

All extensions include an init_app method for the application factory pattern. It’s going to look more complicated, but it’s worth it:

# __init__.py
def create_app(debug=True):
    app = Flask(__name__)
    # configuration of app here, can use `debug`, yay!
    from .models import db
    db.init_app(app)
    return app

# models.py
db = SQLAlchemy()

Despite the name, application factories aren’t that complicated, and the documentation has improved significantly since 2016. It’s much easier to start with an application factory than try and reorganise a project later, so I’d recommend it from day one.

The application factory pattern makes configuration easy, as well as helping to avoid circular imports. Because imports can be performed when the app is created, this is an inversion of the usual import order. Instead of app being imported by everything, everything (extensions, models, views/blueprints) is imported by the factory and bound together. Speaking of blueprints…

Blueprints

In a normal project, the number of views, and templates, and models, and forms, etc. will grow. You can (hackily) separate them out, but wouldn’t it be nice to have something that groups related views, templates, models, and forms? Blueprints! They are a great way to modularly compose Flask applications, which will help scale up projects.

The documentation on blueprints is pretty good, and they’re very analogous to views, so I’m brazenly going to gloss over that. But let it be said that refactoring an application to use blueprints is hard. So the upfront time investment is worth it, and along with application factories, I do it even for small proof of concept projects.

Python, Flask

Newer Older