Flask Signals: The Secret Messenger System 🚀
Imagine you have a house with many rooms. When the doorbell rings, everyone in the house hears it - they don’t need to watch the door constantly. Flask Signals work exactly like that doorbell!
🎯 What Are Flask Signals?
Think of signals like announcements in a school. When the principal makes an announcement over the speaker, every classroom hears it at the same time. They don’t need to keep asking “Did something happen?”
Flask Signals let different parts of your app “announce” when something happens, and other parts can “listen” and react.
Why Use Signals?
| Without Signals 😓 | With Signals 😊 |
|---|---|
| Parts of your app constantly check for changes | Parts listen and react automatically |
| Code gets tangled together | Code stays clean and separate |
| Adding new features means changing old code | New features just “subscribe” |
📻 Built-in Flask Signals
Flask comes with ready-made signals - like pre-installed doorbells! Here are the most important ones:
The Request Family 📨
from flask import request_started
from flask import request_finished
from flask import request_tearing_down
# These fire during every request!
Simple Example:
from flask import Flask
from flask import request_started
app = Flask(__name__)
def log_request(sender, **extra):
print("A request just started!")
# Connect our function to the signal
request_started.connect(log_request, app)
What Each Built-in Signal Does
graph TD A["User Makes Request"] --> B["request_started"] B --> C["Your App Processes"] C --> D["request_finished"] D --> E["request_tearing_down"] E --> F["Response Sent"] style B fill:#4CAF50,color:white style D fill:#2196F3,color:white style E fill:#FF9800,color:white
| Signal | When It Fires | Real Use |
|---|---|---|
request_started |
Request begins | Log who visited |
request_finished |
Response ready | Track timing |
request_tearing_down |
Cleanup time | Close connections |
got_request_exception |
Error occurred | Send alert |
template_rendered |
Template done | Debug templates |
🔨 Creating Custom Signals
Sometimes the built-in doorbells aren’t enough. You want your own special announcement system!
Step 1: Import the Signal Maker
from blinker import signal
# Create your custom signal
user_logged_in = signal('user-logged-in')
Step 2: Use Your Signal
Think of it like this:
- You create a bell named “user-logged-in”
- Anyone can ring this bell
- Anyone can listen for this bell
from blinker import signal
# Create the signal (the bell)
order_placed = signal('order-placed')
# Now you can use it anywhere!
Complete Example
from flask import Flask
from blinker import signal
app = Flask(__name__)
# 1. Create custom signal
user_signed_up = signal('user-signed-up')
# 2. This will run when signal fires
def send_welcome_email(sender, **data):
user = data.get('user')
print(f"Sending welcome to {user}!")
# 3. Connect listener to signal
user_signed_up.connect(send_welcome_email)
@app.route('/signup')
def signup():
# 4. Fire the signal!
user_signed_up.send(app, user="Alice")
return "Welcome!"
👂 Subscribing to Signals
Subscribing = “I want to hear that announcement!”
Method 1: Using .connect()
from flask import request_started
def my_listener(sender, **extra):
print("I heard the signal!")
# Subscribe to the signal
request_started.connect(my_listener, app)
Method 2: Using Decorators
from flask import template_rendered
@template_rendered.connect_via(app)
def log_template(sender, template, **extra):
print(f"Template {template.name} rendered!")
The Key Parts
graph LR A["Signal"] -->|connect| B["Your Function"] B -->|receives| C["sender"] B -->|receives| D["extra data"] style A fill:#E91E63,color:white style B fill:#9C27B0,color:white
| Parameter | What It Is | Example |
|---|---|---|
sender |
Who sent the signal | Your Flask app |
**extra |
Any extra data sent | User info, etc. |
Subscribing to Specific Senders Only
# Only listen when THIS app sends it
request_started.connect(my_func, app)
# Listen to ALL senders
request_started.connect(my_func)
📤 Sending Signals
Sending = “Ring the bell! Make the announcement!”
Basic Send
from blinker import signal
# Create signal
task_completed = signal('task-completed')
# Send it! (ring the bell)
task_completed.send(app)
Sending With Data
# Send extra information with the signal
task_completed.send(
app,
task_name="Download",
status="success",
time_taken=5.2
)
Where to Send Signals
from flask import Flask
from blinker import signal
app = Flask(__name__)
payment_received = signal('payment-received')
@app.route('/pay')
def process_payment():
# ... payment logic ...
# Send signal with data
payment_received.send(
app,
amount=99.99,
customer="Bob"
)
return "Payment done!"
The Send Pattern
graph TD A["Something Happens"] --> B["Call signal.send"] B --> C["Pass sender"] B --> D["Pass extra data"] C --> E["All Listeners Receive It"] D --> E style B fill:#00BCD4,color:white style E fill:#8BC34A,color:white
🎛️ Signal Handlers
A handler is just a function that runs when a signal fires. Think of handlers as workers who respond to announcements.
Anatomy of a Handler
def my_handler(sender, **kwargs):
# sender = who sent the signal
# kwargs = extra data dictionary
print(f"Got signal from {sender}")
print(f"Data: {kwargs}")
Real Handler Examples
Example 1: Logging Handler
def log_handler(sender, **kwargs):
timestamp = kwargs.get('time')
action = kwargs.get('action')
print(f"[{timestamp}] {action}")
# Connect it
user_action.connect(log_handler)
Example 2: Email Handler
def email_handler(sender, **kwargs):
email = kwargs.get('email')
event = kwargs.get('event')
if event == 'signup':
send_email(email, "Welcome!")
elif event == 'purchase':
send_email(email, "Thanks!")
purchase_made.connect(email_handler)
Multiple Handlers, One Signal
from blinker import signal
order_placed = signal('order-placed')
# Handler 1: Update inventory
def update_stock(sender, **kw):
print("Reducing stock...")
# Handler 2: Notify warehouse
def notify_warehouse(sender, **kw):
print("Alerting warehouse...")
# Handler 3: Send confirmation
def send_confirmation(sender, **kw):
print("Emailing customer...")
# All three listen to same signal!
order_placed.connect(update_stock)
order_placed.connect(notify_warehouse)
order_placed.connect(send_confirmation)
# When you send...
order_placed.send(app, item="Book", qty=1)
# ALL three handlers run!
graph TD A["order_placed.send"] --> B["update_stock"] A --> C["notify_warehouse"] A --> D["send_confirmation"] style A fill:#FF5722,color:white style B fill:#3F51B5,color:white style C fill:#3F51B5,color:white style D fill:#3F51B5,color:white
🎓 Putting It All Together
Here’s a complete mini-app showing everything:
from flask import Flask
from blinker import signal
app = Flask(__name__)
# 1. CREATE custom signals
user_registered = signal('user-registered')
user_logged_in = signal('user-logged-in')
# 2. HANDLERS (signal workers)
def welcome_email(sender, **kw):
print(f"Welcome {kw['name']}!")
def log_login(sender, **kw):
print(f"{kw['name']} logged in")
def give_bonus(sender, **kw):
print(f"Bonus points for {kw['name']}!")
# 3. SUBSCRIBE handlers to signals
user_registered.connect(welcome_email)
user_registered.connect(give_bonus)
user_logged_in.connect(log_login)
# 4. SEND signals in routes
@app.route('/register/<name>')
def register(name):
user_registered.send(app, name=name)
return f"Registered {name}!"
@app.route('/login/<name>')
def login(name):
user_logged_in.send(app, name=name)
return f"Hello {name}!"
💡 Quick Tips
| Do This ✅ | Not This ❌ |
|---|---|
| Keep handlers fast | Slow handlers block everything |
| Pass only needed data | Don’t send huge objects |
| Use clear signal names | Vague names cause confusion |
| Disconnect when done | Memory leaks from orphan handlers |
Disconnecting Signals
# When you're done listening
my_signal.disconnect(my_handler)
🏆 You Did It!
You now understand Flask Signals:
- Built-in signals - Flask’s ready-made announcements
- Custom signals - Your own bells to ring
- Subscribing - Listening for announcements
- Sending - Making announcements
- Handlers - Workers who respond
Remember the doorbell analogy: Create a bell, connect listeners, ring when needed. Everyone hears, everyone reacts!
graph LR A["Create Signal"] --> B["Subscribe Handlers"] B --> C["Send When Ready"] C --> D["Handlers React"] D --> E["App Stays Clean!"] style E fill:#4CAF50,color:white
