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.
# 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.
# 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.
# 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.
# 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.
create_app, Factory Pattern, Testing, Configuration, Extension Init