Home Articles Categories Series
Pythonise Just now

Application factory pattern | Learning Flask Ep. 30

Building scalable Flask applications from the start using the application factory pattern, blueprints and the current_app proxy


Article Posted on by in Flask
Julian Nash · 2 months ago in Flask

The application factory pattern in combination with Blueprints and the current_app proxy, provide a scalable, consistent and pluggable structural foundation for many Flask applications.

In this article, I'll give you a quick high level overview and a few examples of using this pattern.

It's common to see many Flask applications start out with the following structure:

app
├── __init__.py
├── views.py
├── static
└──templates

In this example, we have a views.py file containing the application routes, static and templates directories for our static assets and HTML templates respectively, along with an __init__.py file to create the app object and register our routes.

A minimal app

In it's most simple form, a Flask application can be created with the following few lines of code:

# app/__init__.py

from flask import Flask

app = Flask(__name__)

from app import views

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

To avoid circular dependency issues, we must import views after creating the app variable, along with any other objects we need to import that reference the app object.

Importing the app object

Many other files in the application will need access to the app object created in the __init__.py file, such as registering routes, logging or accessing config values.

To do so, we must import it first:

# app/views.py

from app import app
from flask import render_template


@app.route("/")
def index():

    app.logger.debug(app.config.get("ENV"))

    return render_template("index.html")

We now have access to the app object for logging, accessing config values and registering the index route.

While this solution works, it's not particularly elgant or scalable, especially when it comes to writing tests for the app.

A better solution is to create a function in the __init__.py file that builds the application object and returns it, often referred to as an application factory.

The application factory

As your Flask application grows, you'll often find the need to register blueprints, dynamically load configuration, add request handlers etc..

The application factory is a function that wraps the creating of the app object and returns it.

Here's an example, we'll go back and refactor our own __init__.py file shortly:

from flask import Flask
from .utils import config

import os

def create_app(testing=False):
    """ Application factory

    Args:
        testing (bool): Will load TestingConfig if True, defaults fo False
    Returns:
        The Flask application object
    """

    app = Flask(__name__)

    # Dynamically load config based on the testing argument or FLASK_ENV environment variable
    flask_env = os.getenv("FLASK_ENV", None)
    if testing:
        app.config.from_object(config.TestingConfig)
    elif flask_env == "development":
        app.config.from_object(config.ProductionConfig)
    elif flask_env == "testing":
        app.config.from_object(config.TestingConfig)
    else:
        app.config.from_object(config.ProductionConfig)

    # Import and register blueprints
    from app.blueprints.views import view
    from app.blueprints.api import api

    app.register_blueprint(view)
    app.register_blueprint(api)

    return app

Unlike the previous example, we're now unable to directly reference the app variable throughout the aplication, so what now?

We should take advantage of Flask's Blueprint feature, replacing any @app.route decorators with the newly created blueprint, along with another Flask feature - current_app.

Accessing the current_app

Having wrapped the app object inside of the create_app function, we still need a way to access it other than calling the function itself.

Flask provides an import called current_app, which acts as a proxy to the current application and can be used as if you were calling app itself - Neat!

Let's recreate the views.py file above using a Blueprint and referencing the current_app proxy:

app/views.py

from flask import Blueprint, render_template
from flask import current_app as app

view = Blueprint("view", __name__)

@view.route("/")
def index():

    app.logger.debug(app.config.get("ENV"))

    return "Hello world!"

As you can see, we didn't have to change much. We can even reference the app object by renaming the import.

Before we can access the route, we need to register the new Blueprint with the application. We'll do this in the create_app function.

Registering the Blueprint in the application factory

I'll now refactor the __init__.py file to include the create_app function and register the Blueprint:

app/views.py

from flask import Flask
from app import config


def create_app(testing=False):

    app = Flask(__name__)

    # Here's a good place to load different configurations based on arguments passed to the create_app function or from environment variables

    if testing:
        app.config.from_object(config.TestingConfig)

    from app.views import view
    app.register_blueprint(view)

    return app

Running the app gives us the same output as before, however in a much more modular, scalable and testable way.

The create_app function can now easily be imported to your Python tests, called and take arguments, providing a dynamic way to load different configurations or trigger different behaviour etc..

Wrapping up

Application factories such as the create_app function shown in this article, combined with the excellent Blueprint feature are the building blocks of robust and scalable Flask applications.

Did you find this article useful?
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License
Contents
Loading...