Flask by Example: Signal Receiver Hooks
Signals allow you to decouple your application logic by subscribing to events. This example shows how to use the `blinker` library to listen for Flask's built-in signals.
Code
from flask import Flask, template_rendered, request_started
from flask.signals import Namespace
app = Flask(__name__)
# 1. Listen to Built-in Signals
def log_template_renders(sender, template, context, **extra):
print(f"Rendering template: {template.name}")
# You could log this to a database or analytics service
# Connect the receiver function to the signal
template_rendered.connect(log_template_renders, app)
# 2. Define Custom Signals
my_signals = Namespace()
user_registered = my_signals.signal('user-registered')
def send_welcome_email(sender, user_email, **extra):
print(f"Sending welcome email to {user_email}...")
# Connect custom signal
user_registered.connect(send_welcome_email)
@app.route('/register/<email>')
def register(email):
# Perform registration logic...
# 3. Send the signal
user_registered.send(app, user_email=email)
return f"Registered {email}"Explanation
Signals allow different parts of your application to communicate without being tightly coupled. Flask comes with several built-in signals, such as template_rendered or request_started, which you can subscribe to. This is useful for logging, analytics, or debugging, as you can "hook" into these events without modifying the core application code.
You can also define your own custom signals using the blinker library (which Flask uses internally). For example, you might define a user_registered signal that triggers a welcome email. The registration route simply sends the signal, and a separate receiver function handles the email logic.
This separation of concerns makes your code cleaner and easier to maintain. The registration logic doesn't need to know about email servers or logging systems; it just announces that an event happened, and any interested subscribers can react accordingly.
Code Breakdown
sender and **extra arguments. The sender is usually the application instance or the object triggering the event.template_rendered.connect(func, app) subscribes your function to the signal. Passing app as the second argument ensures you only receive signals from that specific application instance.user_registered.send() triggers the event. All connected functions are executed synchronously, so be careful not to put slow operations (like sending real emails) directly in the receiver without using a task queue.
