Application Factory

Difficulty: Intermediate

The application factory pattern is the recommended way to create Flask applications, especially for larger projects. Instead of creating the Flask app at module level, you define a create_app() function that creates and configures the app. This pattern solves several problems and is essential for testing, multiple configurations, and avoiding circular imports.

The core idea is simple: instead of 'app = Flask(__name__)' at the module level, you wrap it in a function. The create_app function typically accepts a configuration object or config name, creates the Flask instance, loads configuration, initializes extensions, registers blueprints, and returns the app.

The factory pattern enables several important capabilities. Testing becomes easy because you can create a fresh app for each test with a test-specific configuration (like using a separate test database). You can run multiple instances of the same app with different configurations. And you avoid the circular import problem that occurs when modules need to import the app object.

Initializing extensions with the factory pattern uses a two-step process called init_app. Instead of creating the extension with the app, you create the extension without an app and call extension.init_app(app) inside the factory. For example, you create 'db = SQLAlchemy()' at the module level and call 'db.init_app(app)' in create_app(). This lets other modules import db without needing the app.

The factory function typically follows this order: create the Flask instance, load configuration from a config object or environment, initialize extensions, register blueprints, register error handlers, and return the app. Flask automatically detects a create_app function and calls it when you use 'flask run'.

The factory pattern works seamlessly with the Flask CLI. When you run 'flask run', Flask looks for a create_app() or make_app() function in the module specified by FLASK_APP. It calls the function and uses the returned app. You can pass configuration through environment variables that the factory reads.

In testing, the factory pattern shines. You create a test fixture that calls create_app with a test configuration, then use app.test_client() to make test requests. Each test gets a fresh app instance with its own database and configuration, ensuring tests are isolated and reproducible.

Code examples

Basic Application Factory

# app/__init__.py
from flask import Flask

def create_app(config_name='default'):
    app = Flask(__name__)

    # Load configuration
    if config_name == 'testing':
        app.config['TESTING'] = True
        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
    else:
        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'

    app.config['SECRET_KEY'] = 'your-secret-key'

    # Register blueprints
    from app.views.main import main_bp
    from app.views.auth import auth_bp
    from app.views.api import api_bp

    app.register_blueprint(main_bp)
    app.register_blueprint(auth_bp, url_prefix='/auth')
    app.register_blueprint(api_bp, url_prefix='/api')

    return app

The create_app function takes an optional config name and returns a configured Flask app. Blueprints are imported inside the function to avoid circular imports. Different configs support testing, development, and production.

Factory with Extension init_app

# app/extensions.py
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_migrate import Migrate

db = SQLAlchemy()
login_manager = LoginManager()
migrate = Migrate()

# app/__init__.py
from flask import Flask
from app.extensions import db, login_manager, migrate

def create_app(config_class='config.DevelopmentConfig'):
    app = Flask(__name__)
    app.config.from_object(config_class)

    # Initialize extensions with the app
    db.init_app(app)
    login_manager.init_app(app)
    migrate.init_app(app, db)

    # Register blueprints
    from app.views import main_bp, auth_bp
    app.register_blueprint(main_bp)
    app.register_blueprint(auth_bp, url_prefix='/auth')

    return app

Extensions are created without an app in extensions.py. The factory calls init_app() to bind them to the app. Other modules can import db from extensions without needing the app, avoiding circular imports.

Using the Factory in Tests

# tests/conftest.py
import pytest
from app import create_app
from app.extensions import db

@pytest.fixture
def app():
    """Create a fresh app for each test."""
    app = create_app('config.TestingConfig')

    with app.app_context():
        db.create_all()
        yield app
        db.drop_all()

@pytest.fixture
def client(app):
    """Test client for making requests."""
    return app.test_client()

# tests/test_api.py
def test_get_users(client):
    response = client.get('/api/users')
    assert response.status_code == 200
    assert response.json == []

def test_create_user(client):
    response = client.post('/api/users',
        json={'name': 'Alice', 'email': 'alice@example.com'}
    )
    assert response.status_code == 201

The factory makes testing easy. Each test gets a fresh app with a test database (in-memory SQLite). The fixture creates tables before each test and drops them after, ensuring test isolation.

Running the Factory App

# run.py (entry point)
from app import create_app

app = create_app()

if __name__ == '__main__':
    app.run(debug=True)

# Or use the Flask CLI:
# export FLASK_APP=app
# flask run

# Flask automatically finds create_app() in the app package
# and calls it. No need for run.py when using the CLI.

# For production with Gunicorn:
# gunicorn 'app:create_app()'

For development, either use run.py or the Flask CLI (which auto-discovers create_app). For production, Gunicorn calls the factory with gunicorn 'app:create_app()'. The factory is called once at startup.

Key points

Concepts covered

create_app, Factory Pattern, Testing, Configuration, Extension Init