Модульные приложения с чертежами

Changelog

Добавлено в версии 0.7.

Flask использует концепцию blueprints для создания компонентов приложений и поддержки общих шаблонов внутри приложения или между приложениями. Blueprints могут значительно упростить работу больших приложений и обеспечить централизованное средство для расширений Flask для регистрации операций над приложениями. Объект Blueprint работает аналогично объекту приложения Flask, но на самом деле он не является приложением. Скорее это набросок того, как построить или расширить приложение.

Почему именно чертежи?

Черновики во Flask предназначены именно для таких случаев:

  • Преобразование приложения в набор чертежей. Это идеально подходит для больших приложений; проект может инстанцировать объект приложения, инициализировать несколько расширений и зарегистрировать коллекцию чертежей.

  • Зарегистрируйте чертеж в приложении по префиксу URL и/или поддомену. Параметры в префиксе URL/поддомене становятся общими аргументами представления (с значениями по умолчанию) для всех функций представления в чертеже.

  • Регистрация чертежа несколько раз в приложении с различными правилами URL.

  • Обеспечьте фильтры шаблонов, статические файлы, шаблоны и другие утилиты с помощью чертежей. В чертеже не обязательно реализовывать приложения или функции представления.

  • Зарегистрируйте чертеж на приложении для любого из этих случаев при инициализации расширения Flask.

Блюпринт во Flask не является подключаемым приложением, потому что на самом деле это не приложение - это набор операций, которые могут быть зарегистрированы в приложении, даже несколько раз. Почему бы не иметь несколько объектов приложения? Вы можете сделать это (см. Диспетчеризация приложений), но ваши приложения будут иметь отдельные конфигурации и будут управляться на уровне WSGI.

Вместо этого блюпринты обеспечивают разделение на уровне Flask, совместно используют конфигурацию приложения и могут изменять объект приложения по мере необходимости, будучи зарегистрированными. Недостатком является то, что вы не можете отменить регистрацию blueprint после создания приложения без необходимости уничтожения всего объекта приложения.

Концепция чертежей

Основная концепция чертежей заключается в том, что они записывают операции, которые должны выполняться при регистрации приложения. Flask связывает функции представления с чертежами при диспетчеризации запросов и генерации URL-адресов от одной конечной точки к другой.

Мой первый чертеж

Вот как выглядит очень простой чертеж. В данном случае мы хотим реализовать чертеж, который выполняет простой рендеринг статических шаблонов:

from flask import Blueprint, render_template, abort
from jinja2 import TemplateNotFound

simple_page = Blueprint('simple_page', __name__,
                        template_folder='templates')

@simple_page.route('/', defaults={'page': 'index'})
@simple_page.route('/<page>')
def show(page):
    try:
        return render_template(f'pages/{page}.html')
    except TemplateNotFound:
        abort(404)

Когда вы привязываете функцию с помощью декоратора @simple_page.route, блюпринт запишет намерение зарегистрировать функцию show на приложении, когда оно будет зарегистрировано позже. Дополнительно он префикснет конечную точку функции именем чертежа, которое было передано конструктору Blueprint (в данном случае также simple_page). Имя blueprint’а не изменяет URL, только конечную точку.

Регистрация чертежей

Как же зарегистрировать этот чертеж? Вот так:

from flask import Flask
from yourapplication.simple_page import simple_page

app = Flask(__name__)
app.register_blueprint(simple_page)

Если вы проверите правила, зарегистрированные в приложении, вы найдете следующие:

>>> app.url_map
Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
 <Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
 <Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>])

Первый, очевидно, из самого приложения для статических файлов. Два других - для функции show чертежа simple_page. Как вы можете видеть, они также имеют префикс с именем чертежа и разделены точкой (.).

Однако чертежи также могут быть установлены в различных местах:

app.register_blueprint(simple_page, url_prefix='/pages')

И, конечно, вот сгенерированные правила:

>>> app.url_map
Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
 <Rule '/pages/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
 <Rule '/pages/' (HEAD, OPTIONS, GET) -> simple_page.show>])

Кроме того, вы можете регистрировать чертежи несколько раз, хотя не каждый чертеж может реагировать на это должным образом. Фактически, это зависит от того, как реализован чертеж, если он может быть установлен более одного раза.

Чертежи для раскроя

Можно зарегистрировать чертеж на другом чертеже.

parent = Blueprint('parent', __name__, url_prefix='/parent')
child = Blueprint('child', __name__, url_prefix='/child')
parent.register_blueprint(child)
app.register_blueprint(parent)

Дочерний blueprint получит имя родителя в качестве префикса к своему имени, а дочерние URL будут иметь префикс URL родителя.

url_for('parent.child.create')
/parent/child/create

Функции, специфичные для Blueprint, до запроса и т.д., зарегистрированные в родительской системе, будут срабатывать для дочерней. Если дочерняя функция не имеет обработчика ошибок, который может обработать данное исключение, будет выполнена попытка обработки родительской функции.

Ресурсы чертежей

Чертежи могут предоставлять и ресурсы. Иногда вы можете захотеть ввести чертеж только ради ресурсов, которые он предоставляет.

Папка с ресурсами

Как и для обычных приложений, считается, что чертежи содержатся в папке. Хотя несколько чертежей могут исходить из одной папки, это не обязательно и обычно не рекомендуется.

Папка определяется по второму аргументу Blueprint, которым обычно является __name__. Этот аргумент указывает, какой логический модуль или пакет Python соответствует чертежу. Если он указывает на фактический пакет Python, то этот пакет (который является папкой в файловой системе) является папкой ресурсов. Если это модуль, то папкой ресурсов будет пакет, в котором содержится модуль. Вы можете обратиться к свойству Blueprint.root_path, чтобы узнать, что такое папка ресурсов:

>>> simple_page.root_path
'/Users/username/TestProject/yourapplication'

Для быстрого открытия источников из этой папки можно использовать функцию open_resource():

with simple_page.open_resource('static/style.css') as f:
    code = f.read()

Статические файлы

Блюпринт может открыть папку со статическими файлами, указав путь к папке в файловой системе с помощью аргумента static_folder. Это либо абсолютный путь, либо относительный к местоположению чертежа:

admin = Blueprint('admin', __name__, static_folder='static')

По умолчанию самая правая часть пути - это место, где он отображается в Интернете. Это можно изменить с помощью аргумента static_url_path. Поскольку здесь папка называется static, она будет доступна по адресу url_prefix blueprint + /static. Если blueprint имеет префикс /admin, то статический URL будет /admin/static.

Конечная точка имеет имя blueprint_name.static. Вы можете генерировать URL к ней с помощью url_for(), как и к статической папке приложения:

url_for('admin.static', filename='style.css')

Однако если у чертежа нет url_prefix, то получить доступ к статической папке чертежа невозможно. Это происходит потому, что URL в этом случае будет /static, а маршрут приложения /static имеет приоритет. В отличие от папок шаблонов, статические папки чертежей не ищутся, если файл не существует в статической папке приложения.

Шаблоны

Если вы хотите, чтобы чертеж раскрывал шаблоны, вы можете сделать это, указав параметр template_folder в конструкторе Blueprint:

admin = Blueprint('admin', __name__, template_folder='templates')

Для статических файлов путь может быть абсолютным или относительным к папке ресурсов чертежа.

Папка шаблона добавляется в путь поиска шаблонов, но с более низким приоритетом, чем папка шаблонов реального приложения. Таким образом, вы можете легко переопределить шаблоны, которые чертеж предоставляет в реальном приложении. Это также означает, что если вы не хотите, чтобы шаблон чертежа был случайно переопределен, убедитесь, что ни один другой шаблон чертежа или реального приложения не имеет такого же относительного пути. Если несколько чертежей предоставляют один и тот же относительный путь к шаблону, первый зарегистрированный чертеж имеет приоритет над остальными.

Так, если у вас есть чертеж в папке yourapplication/admin и вы хотите отрисовать шаблон 'admin/index.html', а в качестве template_folder вы указали templates, то вам придется создать такой файл: yourapplication/admin/templates/admin/index.html. Причина дополнительной папки admin заключается в том, чтобы избежать переопределения нашего шаблона шаблоном с именем index.html в реальной папке шаблонов приложения.

Еще раз повторю: если у вас есть чертеж с именем admin и вы хотите отобразить шаблон с именем index.html, который специфичен для этого чертежа, лучше всего расположить шаблоны следующим образом:

yourpackage/
    blueprints/
        admin/
            templates/
                admin/
                    index.html
            __init__.py

Затем, когда вы хотите отобразить шаблон, используйте admin/index.html в качестве имени для поиска шаблона. Если у вас возникнут проблемы с загрузкой нужных шаблонов, включите конфигурационную переменную EXPLAIN_TEMPLATE_LOADING, которая даст указание Flask выводить шаги, которые он выполняет для поиска шаблонов при каждом вызове render_template.

Создание URL-адресов

Если вы хотите сделать ссылку с одной страницы на другую, вы можете использовать функцию url_for() так же, как вы обычно это делаете, только в качестве префикса конечной точки URL указывается имя чертежа и точка (.):

url_for('admin.index')

Кроме того, если вы находитесь в функции просмотра чертежа или визуализированного шаблона и хотите сделать ссылку на другую конечную точку того же чертежа, вы можете использовать относительные перенаправления, префикснув конечную точку точкой only:

url_for('.index')

Это приведет к ссылке на admin.index, например, в случае, если текущий запрос был отправлен на любую другую конечную точку admin blueprint.

Обработчики ошибок чертежей

Blueprints поддерживает декоратор errorhandler так же, как и объект приложения Flask, поэтому легко создавать пользовательские страницы ошибок, специфичные для Blueprint.

Вот пример исключения «404 Page Not Found»:

@simple_page.errorhandler(404)
def page_not_found(e):
    return render_template('pages/404.html')

Большинство обработчиков ошибок будут работать, как и ожидалось; однако есть оговорка относительно обработчиков исключений 404 и 405. Эти обработчики ошибок вызываются только из соответствующего оператора raise или вызова abort в другой функции представления блюпринта; они не вызываются, например, при неправильном доступе к URL. Это происходит потому, что блюпринт не «владеет» определенным пространством URL, поэтому у экземпляра приложения нет возможности узнать, какой обработчик ошибок блюпринта он должен запустить, если ему будет предоставлен неверный URL. Если вы хотите использовать различные стратегии обработки этих ошибок на основе префиксов URL, они могут быть определены на уровне приложения с помощью прокси-объекта request:

@app.errorhandler(404)
@app.errorhandler(405)
def _handle_api_error(ex):
    if request.path.startswith('/api/'):
        return jsonify(error=str(ex)), ex.code
    else:
        return ex

См. Работа с ошибками приложения.

Вернуться на верх