Flask Application Factory

The Application Factory pattern moves app creation into a function. Instead of creating the Flask app at the module level, a function called create_app() builds and returns it. This pattern supports multiple configurations (testing, development, production) and fixes circular import problems in larger projects.

The Problem with Module-Level App Creation

In a small app, this works fine:

app = Flask(__name__)  # module-level creation

In a larger app with blueprints and extensions, every module imports app from this file. If those modules also define models or routes that app.py imports, Python hits a circular import — Module A imports Module B which imports Module A, creating an infinite loop.

The Factory Function Solution


# app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager

db = SQLAlchemy()
login_manager = LoginManager()

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

    # Load config
    from app.config import config
    app.config.from_object(config[config_name])

    # Initialize extensions
    db.init_app(app)
    login_manager.init_app(app)

    # Register blueprints
    from app.auth  import auth  as auth_bp
    from app.blog  import blog  as blog_bp
    app.register_blueprint(auth_bp, url_prefix='/auth')
    app.register_blueprint(blog_bp, url_prefix='/blog')

    return app

How Extensions Work with the Factory

Extensions like SQLAlchemy and LoginManager are created without an app object (db = SQLAlchemy()). Later, db.init_app(app) binds them to the specific app instance. This separation allows the same extension objects to be imported anywhere without a circular dependency.

Without Factory:
  app = Flask()
  db = SQLAlchemy(app)  ← db tied to one specific app

With Factory:
  db = SQLAlchemy()     ← db created standalone
  db.init_app(app)      ← bound at runtime inside create_app()

Configuration Classes

Create a config.py file with separate classes for each environment:

import os

class Config:
    SECRET_KEY         = os.environ.get('SECRET_KEY') or 'dev-secret'
    SQLALCHEMY_TRACK_MODIFICATIONS = False

class DevelopmentConfig(Config):
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///dev.db'

class TestingConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'

class ProductionConfig(Config):
    DEBUG = False
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')

config = {
    'development': DevelopmentConfig,
    'testing':     TestingConfig,
    'production':  ProductionConfig,
    'default':     DevelopmentConfig
}

The Entry Point

Create a run.py (or wsgi.py) file at the project root:

import os
from app import create_app

app = create_app(os.environ.get('FLASK_ENV', 'development'))

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

Complete Project Structure with Factory

myapp/
├── run.py               ← entry point
├── config.py            ← environment configs
├── requirements.txt
└── app/
    ├── __init__.py      ← create_app() lives here
    ├── models.py        ← SQLAlchemy models
    ├── auth/
    │   ├── __init__.py
    │   └── routes.py
    ├── blog/
    │   ├── __init__.py
    │   └── routes.py
    ├── templates/
    └── static/

Testing with the Factory

The factory makes testing simple — create a fresh app instance with the test configuration for each test suite:

import pytest
from app import create_app, db

@pytest.fixture
def app():
    app = create_app('testing')
    with app.app_context():
        db.create_all()
        yield app
        db.drop_all()

@pytest.fixture
def client(app):
    return app.test_client()

Each test gets an isolated in-memory database. Tests cannot affect each other's data.

Summary

The Application Factory pattern wraps Flask app creation in a create_app() function. Extensions are initialized separately and bound with .init_app(app). Different config classes handle development, testing, and production environments. Blueprints register inside the factory. This pattern eliminates circular imports, enables parallel test environments, and gives you full control over how each instance of your app is configured.

Leave a Comment

Your email address will not be published. Required fields are marked *