• Flask 信号


    信号(Signal)就是两个独立的模块用来传递消息的方式,它有一个消息的发送者Sender,还有一个消息的订阅者Subscriber。信号的存在使得模块之间可以摆脱互相调用的模式,也就是解耦合。发送者无需知道谁会接收消息,接收者也可自由选择订阅何种消息

    Flask的信号功能是基于Python消息分发组件Blinker之上的,

    pip install blinker

    订阅一个Flask信号

    代码

    from flask import Flask
    from flask import render_template, request
     
    app = Flask(__name__)
     
    @app.route('/hello')
    @app.route('/hello/<name>')
    def hello(name=None):
        return render_template('hello.html', name=name)
     
    def print_template_rendered(sender, template, context, **extra):
        print 'Using template: %s with context: %s' % (template.name, context)
        print request.url
    template_rendered.connect(print_template_rendered, app)
    if __name__ == '__main__':
        app.run(host='0.0.0.0', debug=True)

    模板

    <!doctype html>
    <title>Hello Sample</title>
    {% if name %}
      <h1>Hello {{ name }}!</h1>
    {% else %}
      <h1>Hello World!</h1>
    {% endif %}

     访问”http://localhost:5000/hello”时,你会在控制台看到:

    Using template: hello.html with context: {'session': , 'request': , 'name': None, 'g': }
    http://localhost:5000/hello

    简单解释下,”flask.template_rendered”是一个信号,更确切的说是Flask的核心信号。当任意一个模板被渲染成功后,这个信号就会被发出。信号的”connect()”方法用来连接订阅者,它的第一个参数就是订阅者回调函数,当信号发出时,这个回调函数就会被调用;第二个参数是指定消息的发送者,也就是指明只有”app”作为发送者发出的”template_rendered”消息才会被此订阅者接收。你可以不指定发送者,这样,任何发送者发出的”template_rendered”都会被接收。一般使用中我们建议指定发送者,以避免接收所有消息。”connect()”方法可以多次调用,来连接多个订阅者。

    再来看看这个回调函数,它有四个参数,前三个参数是必须的。

    1. sender: 获取消息的发送者 app
    2. template: 被渲染的模板对象hello.html
    3. context: 当前请求的上下文环境
    4. **extra: 匹配任意额外的参数。如果上面三个存在,这个参数不加也没问题。但是如果你的参数少于三个,就一定要有这个参数。一般习惯上加上此参数。

    我们在回调函数中,可以获取请求上下文,也就是它在一个请求的生命周期和线程内。所以,我们可以在函数中访问request对象。

    Flask同时提供了信号装饰器来简化代码,上面的信号订阅也可以写成:

    from flask import template_rendered, request
     
    @template_rendered.connect_via(app)
    def with_template_rendered(sender, template, context, **extra):
        print 'Using template: %s with context: %s' % (template.name, context)
        print request.url

    ”connect_via()”方法中的参数指定了发送者,不加的话就指所有发送者。

    Flask核心信号(Core Signals)

      • request_started

    1、请求开始时发送。回调函数参数:

    • sender: 消息的发送者

    2、request_finished

    请求结束后发送。回调函数参数:

    • sender: 消息的发送者
    • response: 待返回的响应对象

    3、got_request_exception

    请求发生异常时发送。回调函数参数:

    • sender: 消息的发送者
    • exception: 被抛出的异常对象

    4、request_tearing_down

    请求被销毁时发送,不管有无异常都会被发送。回调函数参数:

    • sender: 消息的发送者
    • exc: 有异常时,抛出的异常对象
      • appcontext_tearing_down

    5、应用上下文被销毁时发送。回调函数参数:

    • sender: 消息的发送者
      • appcontext_pushed

    6、应用上下文被压入”_app_ctx_stack”栈后发送。回调函数参数:

    • sender: 消息的发送者

    7、appcontext_popped

    应用上下文从”_app_ctx_stack”栈中弹出后发送。回调函数参数:

    • sender: 消息的发送者
      • message_flashed

    8、消息闪现时发送。回调函数参数:

    • sender: 消息的发送者
    • message: 被闪现的消息内容
    • category: 被闪现的消息类别

    注,所有回调函数都建议加上”**extra”作为最后的参数。

    同上下文Hook函数的区别

    信号的目的只是为了通知订阅者某件事情发生了,但它不鼓励订阅者去修改数据。比如”request_finished”信号回调函数无需返回response对象,而”after_request”修饰的Hook函数必须返回response对象。

    ########## Capture flask core signals ##########
    @request_started.connect_via(app)
    def print_request_started(sender, **extra):
        print 'Signal: request_started'
     
    @request_finished.connect_via(app)
    def print_request_finished(sender, response, **extra):
        print 'Signal: request_finished'
     
    @request_tearing_down.connect_via(app)
    def print_request_tearingdown(sender, exc, **extra):
        print 'Signal: request_tearing_down'
     
    ########## Request Context Hook ##########
    @app.before_request
    def before_request():
        print 'Hook: before_request'
     
    @app.after_request
    def after_request(response):
        print 'Hook: after_request'
        return response
     
    @app.teardown_request
    def teardown_request(exception):
        print 'Hook: teardown_request'

    运行结果如下:

    Signal: request_started
    Hook: before_request
    Hook: after_request
    Signal: request_finished
    Hook: teardown_request
    Signal: request_tearing_down

    自定义信号

    引入Blinker的库

    from blinker import Namespace
     
    signals = Namespace()
    index_called = signals.signal('index-called')

    我们在全局定义了一个”index_called”信号对象,表示根路径被访问了。然后我们在根路径的请求处理中发出这个信号

    @app.route('/')
    def index():
        index_called.send(current_app._get_current_object(), method=request.method)
        return 'Hello Flask!'

    发送信号消息的方法是”send()”,它必须包含一个参数指向信号的发送者。这里我们使用了”current_app._get_current_object()”来获取应用上下文中的app应用对象。

    这样每次客户端访问根路径时,都会发送”index_called”信号。”send()”方法可以有多个参数,从第二个参数开始是可选的,如果你要提供,就必须是key=value形式。而这个key就可以在订阅回调函数中接收。这个例子中,我们传递了请求的方法。

    现在我们来定义订阅回调函数:

    def log_index_called(sender, method, **extra):
        print 'URL "%s" is called with method "%s"' % (request.url, method)
     
    index_called.connect(log_index_called, app)

    简化

    @index_called.connect_via(app)
    def log_index_called(sender, method, **extra):
        print 'URL "%s" is called with method "%s"' % (request.url, method)
  • 相关阅读:
    11 [异常]-try...except
    3-4 网络编程 练习题
    31 选课系统
    3-3 面向对象 本章总结
    3-2 面向对象中级练习题
    3-1 面向对象练习题
    11 [面向对象]-面向对象的软件开发
    10 [面向对象]-元类
    App集成极光推送开发流程[关键步骤]
    App集成极光推送步骤
  • 原文地址:https://www.cnblogs.com/Erick-L/p/7007108.html
Copyright © 2020-2023  润新知