Introduction to the Flask signal mechanism (signals) in the Python Web framework, flasksignals
Signal (signals)
The Flask signal (signals, or event hooking) allows a specific sender to notify the subscriber of what happened (since we know what happened, we can know what to do next ).
Flask provides some signals (core signals) and other extensions provide more signals. The signal is used to notify the subscriber, instead of encouraging the subscriber to modify the data. For more information, see the documentation.
The signal depends on the Blinker library.
Hook (hooks)
The Flask hook (usually appears in the blueprint or existing method of the application, such as some built-in decorators, such as before_request) does not require the Blinker library and allows you to change the request object (request) or the response object (response ). These changes the behavior of the application (or blueprint. For example, before_request.
The signal looks like a hook. However, they work differently. For example, the core before_request () handler executes the request in a specific order and can discard the request before returning a response. In contrast, all signal processors are unordered and do not modify any data.
In general, hooks are used to change behaviors (such as identity authentication or error handling), while signals are used to record events (such as logging ).
Create Signal
Because the signal depends on the Blinker library, make sure that it has been installed.
If you want to use signals in your own applications, you can directly use the Blinker library. The most common use case is to name a custom Namespace signal. This is also recommended in most cases:
Copy codeThe Code is as follows:
From blinker import Namespace
My_signals = Namespace ()
Now you can create a new signal like this:
Copy codeThe Code is as follows:
Model_saved = my_signals.signal ('model-saved ')
Here we use a unique signal name and simplify debugging. You can use the name attribute to access the signal name.
For extension developers:
If you are writing a Flask extension, you can use the flask. signals. Namespace class to elegantly reduce the impact of missing Blinker installation.
Subscription Signal
You can use the connect () method of signals to subscribe to signals. The first parameter is the function to be called when the signal is sent. The second parameter is optional and used to determine the sender of the signal. A single signal can have multiple subscribers. You can use the disconnect () method to Unsubscribe the signal.
For all core Flask signals, the sender is an application that sends signals. When you subscribe to a signal, make sure that you also provide a sender unless you really want to listen to the signals of all applications. This is especially true when you develop an extension.
For example, here is an assistant context manager used to find out which template is rendered and input the template variables in the unit test:
Copy codeThe Code is as follows:
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)
Now you can easily pair with the test client:
Copy codeThe Code is as follows:
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
Make sure that the subscription uses an additional ** extra parameter, so that your call will not fail when Flask introduces new parameters to the signal.
In the Code, all rendered templates from the with block app are now recorded to the templates variable. Whenever a template is rendered, it is added to the context of the template object.
In addition, there is a convenient helper method (connected_to () that allows you to temporarily subscribe functions to signals using its own context manager. Because the returned value of the context manager cannot be specified by that method, you need to pass the list as a parameter:
Copy codeThe Code is as follows:
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)
The above example looks like this:
Copy codeThe Code is as follows:
Templates = []
With captured_templates (app, templates, ** extra ):
...
Template, context = templates [0]
Send signal
If you want to send a signal, you can call the send () method. It accepts a sender as the first parameter and some optional keyword parameters forwarded to the signal User:
Copy codeThe Code is as follows:
Class Model (object ):
...
Def save (self ):
Model_saved.send (self)
Always try to select a suitable sender. If you have a class that sends signals, use self as the sender. If you send a signal from a random function, use current_app. _ get_current_object () as the sender.
Decoration device based on signal subscription
In Blinker 1.1, you can easily subscribe to signals by using the new connect_via () modifier:
Copy codeThe Code is as follows:
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)
Example
Template Rendering
The template_rendered signal is the core Flask signal.
When a template is successfully rendered, this signal will be sent. This signal is called together with the template and context Dictionary (called context) of the template instance.
Signal sending:
Copy codeThe Code is as follows:
Def _ render (template, context, app ):
"Renders the template and fires the signal """
Rv = template. render (context)
Template_rendered.send (app, template = template, context = context)
Return rv
Subscription example:
Copy codeThe Code is as follows:
Def log_template_renders (sender, template, context, ** extra ):
Sender. logger. debug ('rendering template "% s" with context % s ',
Template. name or 'string template ',
Context)
From flask import template_rendered
Template_rendered.connect (log_template_renders, app)
Account tracking
User_logged_in is the signal defined in Flask-User. After the User successfully logs in, the signal will be sent.
Sending signal:
Copy codeThe Code is as follows:
# Send user_logged_in signal
User_logged_in.send (current_app. _ get_current_object (), user = user)
The following example shows how to track logon times and logon IP addresses:
Copy codeThe Code is as follows:
# This code has not been tested
From flask import request
From flask_user.signals import user_logged_in
@ User_logged_in.connect_via (app)
Def _ track_logins (sender, user, ** extra ):
User. login_count + = 1
User. last_login_ip = request. remote_addr
Db. session. add (user)
Db. session. commit ()
Summary
Signals allow you to securely subscribe to them in an instant. For example, these temporary subscriptions are very helpful for testing. When using signals, do not cause exceptions to the signal subscriber (receiver) because exceptions may interrupt the program.