Flask blueprints: типичные ошибки в структуре приложения
Blueprints в Flask обещают модульность, но на практике приводят к циклическим импортам, потерянным шаблонам и нечитаемой фабрике приложения. Разбираем пять конкретных ошибок — с кодом, который их воспроизводит, и кодом, который их исправляет.

Почему blueprints ломаются не при написании, а при росте
Flask даёт полную свободу в организации файлов — и это главный источник проблем. Пока в проекте три эндпоинта, всё живёт в одном app.py. Как только появляются авторизация, API и админка, разработчик создаёт blueprints — и натыкается на одни и те же грабли, которые документация Flask описывает вскользь. Ниже — пять ошибок, которые встречаются чаще всего в проектах на Flask 2.x–3.x, и конкретные способы их избежать.
Ошибка 1. Циклический импорт через глобальный app
Самый частый сценарий: app.py создаёт экземпляр приложения и импортирует модуль с blueprint, а тот модуль импортирует app обратно — чтобы получить конфиг, логгер или расширение вроде SQLAlchemy.
Как выглядит проблема
# app.py
from flask import Flask
app = Flask(__name__)
from routes.users import users_bp # импорт blueprint
app.register_blueprint(users_bp)
# routes/users.py
from app import app # обратный импорт — циклическая зависимость
from flask import Blueprint
users_bp = Blueprint('users', __name__)
@users_bp.route('/me')
def me():
app.logger.info('...') # ради этого и тянули app
return 'ok'
Python не всегда бросит ImportError — иногда модуль загрузится частично и вы получите AttributeError на ровном месте. Отладка займёт больше времени, чем хотелось бы.
Как исправить: фабрика приложения
Вместо глобального app — функция create_app, которая создаёт экземпляр, регистрирует blueprints и возвращает готовое приложение. Blueprint-модулям экземпляр не нужен: для логгера используйте current_app, для расширений — отложенную инициализацию.
# app/__init__.py (Flask 3.x)
from flask import Flask
from app.extensions import db
def create_app():
application = Flask(__name__)
application.config.from_prefixed_env() # переменные FLASK_*
db.init_app(application) # отложенная инициализация
from app.routes.users import users_bp
application.register_blueprint(users_bp, url_prefix='/users')
return application
# app/extensions.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy() # без привязки к приложению
# app/routes/users.py
from flask import Blueprint, current_app
from app.extensions import db
users_bp = Blueprint('users', __name__)
@users_bp.route('/me')
def me():
current_app.logger.info('запрос /me') # вместо app.logger
return 'ok'
Проверка: если нигде в проекте, кроме create_app, не написано app = Flask(__name__) — цикла не будет.
Правило одного экземпляра: Flask-приложение создаётся ровно в одном месте — в фабрике. Всё остальное получает его черезcurrent_appилиinit_app.
Ошибка 2. Все blueprints в одном пакете без разделения ответственности
Проект растёт, и в каталоге routes/ появляются users.py, orders.py, admin.py, api_v1.py, api_v2.py, webhooks.py. Каждый файл — blueprint, но все делят общие модели, формы и утилиты, которые лежат где-то «рядом». Через полгода никто не может сказать, какой файл к чему относится.
Структура, которая масштабируется
Каждый blueprint — отдельный пакет со своими маршрутами, формами и шаблонами:
app/
├── __init__.py # create_app
├── extensions.py # db, migrate, mail
├── models/ # общие модели
│ ├── user.py
│ └── order.py
├── users/
│ ├── __init__.py # users_bp = Blueprint(...)
│ ├── routes.py
│ ├── forms.py
│ └── templates/users/
│ └── profile.html
├── orders/
│ ├── __init__.py
│ ├── routes.py
│ └── templates/orders/
│ └── list.html
└── admin/
├── __init__.py
├── routes.py
└── templates/admin/
└── dashboard.html
Ключевой момент — шаблоны внутри подкаталога с именем blueprint. Без этого подкаталога Flask при рендере profile.html может взять одноимённый файл из другого blueprint. Об этом — следующая ошибка.
Ошибка 3. Конфликт шаблонов между blueprints
Flask ищет шаблоны сначала в каталоге приложения (app/templates/), потом — в каталогах blueprints. Если два blueprint регистрируют шаблон list.html, отрендерится тот, что нашёлся первым. Ошибки не будет — будет неправильная страница.
Решение
- При создании blueprint указывайте
template_folder:# app/orders/__init__.py from flask import Blueprint orders_bp = Blueprint( 'orders', __name__, template_folder='templates', # путь относительно пакета ) - Внутри
templates/создайте подкаталог с именем blueprint:templates/orders/list.html. - В коде рендерите с префиксом:
render_template('orders/list.html').
Проверка: выполните grep -rn "render_template" app/ и убедитесь, что ни один вызов не использует «голое» имя файла без префикса каталога.
Ошибка 4. url_for без имени blueprint
Внутри blueprint вызов url_for('me') найдёт маршрут, только если имя глобально уникально. При добавлении второго blueprint с маршрутом me Flask бросит BuildError или вернёт неправильный URL.
Правильный синтаксис
# внутри blueprint 'users'
url_for('users.me') # полное имя: blueprint.endpoint
# внутри того же blueprint — можно с точкой:
url_for('.me') # относительное имя текущего blueprint
В шаблонах Jinja — то же самое: {{ url_for('users.me') }}. Привычка сразу писать с префиксом экономит час отладки, когда проект вырастет.
Ошибка 5. Разбухшая фабрика create_app
Фабрика, которая начиналась с десяти строк, через год содержит регистрацию двадцати blueprints, настройку логирования, обработчики ошибок, middleware CORS, Celery, кеш, rate limiter и инициализацию пяти расширений. Функция на 200 строк — это не фабрика, а свалка.
Как разгрузить
- Расширения — в отдельный модуль
extensions.py. Фабрика вызывает толькоinit_app. - Регистрация blueprints — вынесите в функцию
register_blueprints(app)в отдельном файле или в самом__init__.py, но отдельной функцией. - Обработчики ошибок — в модуль
errors.py, который экспортирует функциюregister_error_handlers(app). - Логирование — в
logging_config.pyсdictConfig.
Цель: create_app остаётся оглавлением, а не реализацией. Десять–пятнадцать строк — оптимум.
Сравнение подходов к структуре
| Критерий | Плоская структура (всё в routes/) | Пакетная структура (blueprint = пакет) |
|---|---|---|
| Порог входа | Минимальный — один файл на blueprint | Чуть выше — нужно создать пакет |
| Риск конфликта шаблонов | Высокий | Низкий при правильных подкаталогах |
| Навигация при 10+ blueprints | Тяжёлая — всё в одном каталоге | Каждый модуль автономен |
| Тестирование изолированно | Сложнее — общие зависимости рядом | Проще — у пакета свои зависимости |
| Когда подходит | Прототип, до 3–4 blueprints | Любой проект, который будет жить дольше месяца |
Когда что выбирать
Если в проекте до трёх-четырёх blueprints и нет отдельных шаблонов для каждого — плоская структура работает и не мешает. Как только появляется второй разработчик или blueprints обрастают собственными формами, моделями и шаблонами — переходите на пакетную структуру. Миграция несложная: переименовать файл в пакет, разложить шаблоны, обновить импорты.
Вложенные blueprints (nested blueprints, доступны с Flask 2.0) — отдельная тема. Они полезны для версионирования API (api_v1 как вложенный в api), но добавляют сложность. Используйте их, когда у вас реально есть иерархия, а не просто группа маршрутов.
Сноска. Application factory (фабрика приложения) — паттерн, при котором экземпляр Flask создаётся внутри функции, а не на уровне модуля. Это устраняет циклические импорты и позволяет создавать несколько экземпляров с разной конфигурацией — например, для тестов.
Что делать дальше
- Проверьте свой проект на циклические импорты:
python -c "from app import create_app; create_app()"должен отработать без ошибок. - Пройдите
grepпоrender_templateиurl_for— убедитесь, что везде есть префикс blueprint. - Измерьте длину
create_app: больше 30 строк — повод разнести по модулям. - Для больших проектов посмотрите в сторону официальной документации по blueprints — там описаны вложенные blueprints и обработка ошибок на уровне blueprint, которые стоит изучить отдельно.
Логотип и фирменный стиль: Три концепции разного характера, палитра с обоснованием, проверка на читаемость в малом размере и в один цвет.
Спросить за 60 ₽Источники: Flask Documentation — Modular Applications with Blueprints, Flask Documentation — Application Factories
Частые вопросы
Можно ли обойтись без фабрики приложения и не получить циклический импорт?
Как отлаживать, если шаблон рендерится не тот, который ожидался?
У меня Flask 1.x — эти советы применимы?
Что если код не запускается после перехода на фабрику — AttributeError у расширений?
Есть ли смысл делать каждый blueprint отдельным Python-пакетом с setup.py?
Как тестировать отдельный blueprint изолированно?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.