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,
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
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
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 -
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
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
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:
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.
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..
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.