Blueprint
Если в приложении только один файл, записать все маршруты вместе не проблема.
Но по мере роста функциональности — модуль пользователей, модуль статей, административная панель — код становится трудно поддерживать.
BlueprintЭто модульное решение, предоставляемое Flask, позволяющее разделить приложение на независимые функциональные единицы.
Зачем нужны blueprints
Основная проблема, которую решают blueprints:
- Организация кодаГруппировка связанных маршрутов, шаблонов и статических файлов вместе
- Повторное использованиеОдин и тот же blueprint можно зарегистрировать в разных приложениях или зарегистрировать несколько раз (с разными префиксами)
- Командная работа: Разные разработчики отвечают за разные модули blueprint, что уменьшает конфликты.
Создание Blueprint
Использование BlueprintBlueprintСоздание класса, использование иFlaskочень похожи — те же декораторы маршрутов, тот же синтаксис функций представления:
Пример
from flask import Blueprint, render_template, request, session, redirect, url_for
# Создать экземпляр blueprint
# Первый параметр "auth" — это имя blueprint (используется для ссылок url_for)
# __name__ сообщает blueprint, где искать шаблоны и статические файлы
bp = Blueprint("auth", __name__)
# Используйте bp.route в blueprint, а не app.route
@bp.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form.get("username", "")
if username:
session["username"] = username
return redirect(url_for("blog.index"))
return render_template("auth/login.html")
@bp.route("/register", methods=["GET", "POST"])
def register():
if request.method == "POST":
username = request.form.get("username", "")
if username:
return redirect(url_for("auth.login"))
return render_template("auth/register.html")
@bp.route("/logout")
def logout():
session.clear()
return redirect(url_for("blog.index"))
Пример
from flask import Blueprint, render_template
bp = Blueprint("blog", __name__)
@bp.route("/")
def index():
# Фиктивные данные статьи
posts = [
{"title": Введение во Flask, "author": "example"},
{"title": Объяснение Blueprint, "author": "EXAMPLE"},
]
return render_template("blog/index.html", posts=posts)
@bp.route("/create", methods=["GET", "POST"])
def create():
return render_template("blog/create.html")
Регистрация Blueprint
Blueprint не вступает в силу автоматически после создания; его необходимо зарегистрировать в приложении:
Пример
from flask import Flask
from auth import bp as auth_bp
from blog import bp as blog_bp
app = Flask(__name__)
app.secret_key = "dev-secret-key"
# Зарегистрируйте Blueprint в приложении
# url_prefix: добавляет общий префикс ко всем маршрутам в Blueprint
app.register_blueprint(auth_bp, url_prefix="/auth")
# Маршруты Blueprint auth становятся: /auth/login, /auth/logout, /auth/register
app.register_blueprint(blog_bp)
# У Blueprint blog нет префикса, маршруты остаются: /, /create
Полная таблица маршрутов после регистрации:
| URL | Blueprint | эндпоинт (используется в url_for) |
|---|---|---|
| / | blog | blog.index |
| /create | blog | blog.create |
| /auth/login | auth | auth.login |
| /auth/register | auth | auth.register |
| /auth/logout | auth | auth.logout |
После регистрации чертежа,url_for()В нём имя конечной точки должно содержать имя чертежа в качестве префикса, напримерurl_for("auth.login")а неurl_for("login")。
Особенности использования url_for в чертежах
Внутри одного чертежа можно использовать относительные ссылки, начинающиеся с точки:
Пример
@bp.route("/")
def index():
# Относительные ссылки внутри одного чертежа (начинающиеся с точки)
login_url = url_for(".login") # Эквивалентно url_for("auth.login")
register_url = url_for(".register") # Эквивалентно url_for("auth.register")
# Ссылка между чертежами требует полной конечной точки
blog_url = url_for("blog.index") # Ссылка на index чертежа blog
Шаблоны и статические файлы чертежа
Чертежи также могут иметь собственные шаблоны и статические файлы.
When a blueprint has its own template folder, Flask will first look in the application'stemplatesпоиск в ней, и если не найдено, то искать в папке, указанной в blueprint.
Пример
bp = Blueprint("auth", __name__, template_folder="templates")
# Организация шаблонов для конкретного blueprint
# Путь: auth/templates/auth/login.html
# При рендеринге: render_template("auth/login.html")
# Такая организация избегает конфликтов имён файлов (login.html может отличаться в auth и blog)
Рекомендуемая структура каталогов blueprint:
myflaskapp/
├── app.py # 应用入口,注册蓝图
├── auth.py # 认证蓝图
├── blog.py # 博客蓝图
├── templates/ # 应用级模板(公共 base.html 等)
│ └── base.html
├── static/ # 应用级静态文件
│ └── style.css
├── auth/ # auth 蓝图的独立资源(可选)
│ └── templates/
│ └── auth/
│ ├── login.html
│ └── register.html
└── blog/ # blog 蓝图的独立资源(可选)
└── templates/
└── blog/
├── index.html
└── create.html
Самый простой подход — не настраивать отдельную папку шаблонов для blueprint, а размещать шаблоны единообразно в корневом каталоге проектаtemplates/Ниже, различайте по подкаталогам:templates/auth/login.html、templates/blog/index.html。
Фабричный паттерн — create_app
По мере увеличения количества blueprint, прямое создание их на верхнем уровне модуляapp = Flask(__name__)приведёт к проблемам циклического импорта.
Паттерн «Фабрика» — лучшая практика для решения этой проблемы:
Пример
from flask import Flask
def create_app():
"""Функция фабрики приложения — возвращает настроенный экземпляр Flask"""
app = Flask(__name__)
app.secret_key = "dev-secret-key"
# Загрузить конфигурацию
app.config.from_pyfile("config.py", silent=True)
# Импортируйте blueprints внутри функции, чтобы избежать циклических импортов
from auth import bp as auth_bp
from blog import bp as blog_bp
# Зарегистрируйте blueprint
app.register_blueprint(auth_bp, url_prefix="/auth")
app.register_blueprint(blog_bp)
return app
Преимущества паттерна «Фабрика»:
- Устранение циклических импортовBlueprints можно импортироватьapp,appВы также можете импортировать blueprints, потому что импорт происходит внутри функции.
- Поддержка тестированияМожно создавать экземпляры app с разными конфигурациями для тестирования
- Многоэкземплярное развёртываниеОдин и тот же код может создавать несколько независимых экземпляров app
После использования паттерна «Фабрика» нужноflask --app "app:create_app()" runзапустить:
$ flask --app "app:create_app()" run
Другие возможности в blueprints
Blueprints также поддерживают декораторы, такие как before_request, errorhandler и т. д., и их использование такое же, как в app:
Пример
def check_login():
"""Проверка статуса входа перед всеми запросами в blueprint auth"""
if request.endpoint not in ("auth.login", "auth.register"):
if "username" not in session:
return redirect(url_for("auth.login"))
@bp.errorhandler(404)
def not_found(error):
"""Страница ошибки 404, предназначенная для blueprint auth"""
return render_template("auth/404.html"), 404
Другие расширенияBlueprint'sbefore_requestОн влияет только на маршруты внутри этого blueprint. Если нужно выполнить логику перед всеми запросами, следует использоватьapp.before_request。