Короткий ответ
Приложение создаётся внутри функции create_app(config=None), которая настраивает app.config, инициализирует расширения и регистрирует Blueprint, возвращая готовый объект Flask; это позволяет создавать несколько независимо сконфигурированных экземпляров приложения, например для тестов и production.
Как это работает подробнее
Вместо создания глобального app = Flask(__name__) на уровне модуля, который неявно захватывает конфигурацию в момент импорта, всё инкапсулируется в функцию. Тесты и разные среды вызывают create_app(test_config) с нужными настройками, получая изолированный экземпляр.
Пример кода
example.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app(config_overrides=None):
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///prod.db"
if config_overrides:
app.config.update(config_overrides)
db.init_app(app)
from .users.routes import bp as users_bp
app.register_blueprint(users_bp)
return app
# В тестах:
# app = create_app({"SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:", "TESTING": True})Что использовать на практике
- Практика. Это особенно важно для расширений типа Flask-SQLAlchemy — в тестах можно передать отдельную тестовую БД (например, SQLite in-memory) без влияния на production-конфигурацию, и создавать несколько приложений в одном процессе (например, для параллельных тестов) без конфликтов глобального состояния.
Подводные камни
- Риск. Инициализируют расширения (
db = SQLAlchemy(app)) прямо при созданииappвнутри фабрики без паттернаinit_app, что усложняет повторное создание приложения с другой конфигурацией в рамках одного процесса; оставляют часть конфигурации на уровне модуля вне фабрики, из-за чего она не переопределяется в тестах.