Spec-Zone.ru › Flask 0.12

Сигналы

Журнал изменений

В версии 0.6.

Начиная с Flask 0.6, в Flask интегрирована поддержка сигналов. Эта поддержка обеспечивается отличной библиотекой blinker и будет корректно работать даже если она недоступна.

Что такое сигналы? Сигналы помогают декуплировать приложения, отправляя уведомления, когда действия происходят в другом месте ядра фреймворка или в другом расширении Flask. Короче говоря, сигналы позволяют определенным отправителям уведомлять подписчиков о произошедшем событии.

Flask поставляется с несколькими сигналами, и другие расширения могут предоставлять больше. Также имейте в виду, что сигналы предназначены для уведомления подписчиков и не должны побуждать подписчиков к изменению данных. Вы заметите, что есть сигналы, которые, по-видимому, выполняют ту же функцию, что и некоторые встроенные декораторы (например, request_started очень похож на before_request()). Однако есть различия в их работе. Обработчик ядра before_request(), например, выполняется в определённом порядке и может прервать обработку запроса, вернув ответ. В отличие от этого, все обработчики сигналов выполняются в неопределённом порядке и не изменяют никаких данных.

Главное преимущество сигналов перед обработчиками заключается в том, что к ним можно безопасно подписываться на мгновение. Эти временные подписки полезны, например, для юнит-тестирования. Предположим, вы хотите узнать, какие шаблоны были отображены в рамках запроса: сигналы позволяют сделать именно это.

Подписка на сигналы

Чтобы подписаться на сигнал, можно использовать метод connect() сигнала. Первый аргумент — функция, которая должна вызываться при отправке сигнала, второй необязательный аргумент задаёт отправителя. Чтобы отписаться от сигнала, можно использовать метод disconnect().

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

Например, вот менеджер контекста, который может использоваться в юнит-тесте для определения отображаемых шаблонов и переменных, передаваемых шаблону:

from flask import template_rendered
from contextlib import contextmanager

@contextmanager
def captured_templates(app):
    recorded = []
    def record(sender, template, context, **extra):
        recorded.append((template, context))
    template_rendered.connect(record, app)
    try:
        yield recorded
    finally:
        template_rendered.disconnect(record, app)

Это можно легко использовать с клиентом для тестирования:

with captured_templates(app) as templates:
    rv = app.test_client().get('/')
    assert rv.status_code == 200
    assert len(templates) == 1
    template, context = templates[0]
    assert template.name == 'index.html'
    assert len(context['items']) == 10

Не забудьте подписаться с дополнительным аргументом **extra, чтобы ваши вызовы не завершались ошибкой, если Flask добавит новые аргументы в сигналы.

Все рендеринг шаблонов в коде, выпущенном приложением app в теле блока with, теперь будут записаны в переменной templates. Всякий раз, когда шаблон отображается, в него добавляются объект шаблона и контекст.

Кроме того, есть удобный вспомогательный метод (connected_to()), который позволяет временно подписать функцию на сигнал с помощью менеджера контекста самостоятельно. Так как возвращаемое значение менеджера контекста не может быть указано таким образом, вы должны передать список в качестве аргумента:

from flask import template_rendered

def captured_templates(app, recorded, **extra):
    def record(sender, template, context):
        recorded.append((template, context))
    return template_rendered.connected_to(record, app)

Пример выше будет выглядеть так:

templates = []
with captured_templates(app, templates, **extra):
    ...
    template, context = templates[0]

Изменения API Blinker

Метод connected_to() появился в Blinker с версией 1.1.

Создание сигналов

Если вы хотите использовать сигналы в собственном приложении, вы можете напрямую использовать библиотеку blinker. Наиболее распространённым случаем использования являются именованные сигналы в пользовательском Namespace.. Это то, что рекомендуется чаще всего:

from blinker import Namespace
my_signals = Namespace()

Теперь вы можете создавать новые сигналы так:

model_saved = my_signals.signal('model-saved')

Имя сигнала делает его уникальным и упрощает отладку. Вы можете получить имя сигнала с помощью атрибута name.

Для разработчиков расширений

Если вы пишете расширение Flask и хотите плавно работать при отсутствии blinker, вы можете сделать это, используя класс flask.signals.Namespace.

Отправка сигналов

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

class Model(object):
    ...

    def save(self):
        model_saved.send(self)

Старайтесь всегда выбирать хороший отправитель. Если у вас есть класс, который отправляет сигнал, передайте self в качестве отправителя. Если вы отправляете сигнал из случайной функции, вы можете передать current_app._get_current_object() в качестве отправителя.

Передача прокси в качестве отправителей

Никогда не передавайте current_app в качестве отправителя сигнала. Используйте current_app._get_current_object() вместо этого. Причина в том, что current_app — это прокси, а не реальный объект приложения.

Сигналы и контекст запроса Flask

Сигналы полностью поддерживают Контекст запроса при получении сигналов. Локальные переменные контекста постоянно доступны между request_started и request_finished, поэтому вы можете полагаться на flask.g и другие, по мере необходимости. Обратите внимание на ограничения, описанные в Отправка сигналов и сигнале request_tearing_down.

Подписка на сигналы с помощью декораторов

С Blinker 1.1 вы также можете легко подписаться на сигналы, используя новый декоратор connect_via():

from flask import template_rendered

@template_rendered.connect_via(app)
def when_template_rendered(sender, template, context, **extra):
    print 'Template %s is rendered with %s' % (template.name, context)

Основные сигналы

Посмотрите на Сигналы для получения списка всех встроенных сигналов.

© 2007–2020 Pallets
Licensed under the BSD 3-clause License.
https://flask.palletsprojects.com/en/0.12.x/signals/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API