Señales¶
Las señales son una forma ligera de notificar a los suscriptores determinados eventos durante el ciclo de vida de la aplicación y de cada solicitud. Cuando se produce un evento, emite la señal, que llama a cada suscriptor.
Las señales son implementadas por la librería Blinker. Consulte su documentación para obtener información detallada. Flask proporciona algunas señales integradas. Las extensiones pueden proporcionar las suyas propias.
Muchas señales reflejan las devoluciones de llamada basadas en decoradores de Flask con nombres similares. Por ejemplo, la señal request_started
es similar al decorador before_request()
. La ventaja de las señales sobre los manejadores es que pueden suscribirse temporalmente, y no pueden afectar directamente a la aplicación. Esto es útil para pruebas, métricas, auditoría, y más. Por ejemplo, si quieres saber qué plantillas fueron renderizadas en qué partes de qué peticiones, hay una señal que te notificará esa información.
Núcleo de las señales¶
Véase Señales para una lista de todas las señales incorporadas. La página Application Structure and Lifecycle también describe el orden en que se ejecutan las señales y los decoradores.
Suscripción a las señales¶
Para suscribirse a una señal, se puede utilizar el método connect()
de una señal. El primer argumento es la función que debe ser llamada cuando se emite la señal, el segundo argumento opcional especifica un emisor. Para darse de baja de una señal, puedes utilizar el método disconnect()
.
Para todas las señales del núcleo de Flask, el remitente es la aplicación que emitió la señal. Cuando te suscribas a una señal, asegúrate de proporcionar también un remitente a menos que realmente quieras escuchar las señales de todas las aplicaciones. Esto es especialmente cierto si estás desarrollando una extensión.
Por ejemplo, aquí hay un gestor de contexto de ayuda que se puede utilizar en una prueba de unidad para determinar qué plantillas se han renderizado y qué variables se han pasado a la plantilla:
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)
Ahora se puede emparejar fácilmente con un cliente de prueba:
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
Asegúrate de suscribirte con un argumento extra **extra
para que tus llamadas no fallen si Flask introduce nuevos argumentos en las señales.
Toda la renderización de plantillas en el código emitido por la aplicación app
en el cuerpo del bloque with
se registrará ahora en la variable templates
. Cada vez que se renderiza una plantilla, el objeto de la plantilla así como el contexto se añaden a ella.
Además, existe un práctico método de ayuda (connected_to()
) que permite suscribir temporalmente una función a una señal con un gestor de contexto propio. Debido a que el valor de retorno del gestor de contexto no puede ser especificado de esa manera, tienes que pasar la lista como un argumento:
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)
El ejemplo anterior quedaría así:
templates = []
with captured_templates(app, templates, **extra):
...
template, context = templates[0]
Creación de señales¶
Si quieres usar señales en tu propia aplicación, puedes usar la librería blinker directamente. El caso de uso más común son las señales con nombre en una Namespace
personalizada. Esto es lo que se recomienda la mayoría de las veces:
from blinker import Namespace
my_signals = Namespace()
Ahora puedes crear nuevas señales así:
model_saved = my_signals.signal('model-saved')
El nombre de la señal aquí la hace única y también simplifica la depuración. Puedes acceder al nombre de la señal con el atributo name
.
Envío de señales¶
Si quieres emitir una señal, puedes hacerlo llamando al método send()
. Acepta un remitente como primer argumento y, opcionalmente, algunos argumentos de palabras clave que se reenvían a los suscriptores de la señal:
class Model(object):
...
def save(self):
model_saved.send(self)
Intenta elegir siempre un buen emisor. Si tienes una clase que está emitiendo una señal, pasa self
como emisor. Si estás emitiendo una señal desde una función aleatoria, puedes pasar current_app._get_current_object()
como emisor.
Pasar proxies como remitentes
Nunca pase current_app
como remitente de una señal. Utiliza current_app._get_current_object()
en su lugar. La razón es que current_app
es un proxy y no el objeto real de la aplicación.
Señales y contexto de solicitud de Flask¶
Las señales son totalmente compatibles con El contexto de la solicitud cuando se reciben señales. Las variables locales de contexto están disponibles de forma consistente entre request_started
y request_finished
, por lo que puede confiar en flask.g
y otros según sea necesario. Tenga en cuenta las limitaciones descritas en Envío de señales y la señal request_tearing_down
.
Suscripciones de señales basadas en decoradores¶
También puedes suscribirte fácilmente a señales utilizando el decorador connect_via()
:
from flask import template_rendered
@template_rendered.connect_via(app)
def when_template_rendered(sender, template, context, **extra):
print(f'Template {template.name} is rendered with {context}')