Blueprint

Если в приложении только один файл, записать все маршруты вместе не проблема.

Но по мере роста функциональности — модуль пользователей, модуль статей, административная панель — код становится трудно поддерживать.

BlueprintЭто модульное решение, предоставляемое Flask, позволяющее разделить приложение на независимые функциональные единицы.


Зачем нужны blueprints

Основная проблема, которую решают blueprints:

  • Организация кодаГруппировка связанных маршрутов, шаблонов и статических файлов вместе
  • Повторное использованиеОдин и тот же blueprint можно зарегистрировать в разных приложениях или зарегистрировать несколько раз (с разными префиксами)
  • Командная работа: Разные разработчики отвечают за разные модули blueprint, что уменьшает конфликты.

Flask Blueprint 蓝图模块架构


Создание Blueprint

Использование BlueprintBlueprintСоздание класса, использование иFlaskочень похожи — те же декораторы маршрутов, тот же синтаксис функций представления:

Пример

# Путь к файлу: auth.py (blueprint аутентификации пользователя)
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"))

Пример

# Путь к файлу: blog.py (blueprint блога)
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 не вступает в силу автоматически после создания; его необходимо зарегистрировать в приложении:

Пример

# Путь к файлу: app.py
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 в чертежах

Внутри одного чертежа можно использовать относительные ссылки, начинающиеся с точки:

Пример

# Внутри auth.py использование относительных ссылок чище
@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.

Пример

# Создание 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__)приведёт к проблемам циклического импорта.

Паттерн «Фабрика» — лучшая практика для решения этой проблемы:

Пример

# Путь к файлу: app.py
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:

Пример

@bp.before_request
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。

Другие расширения