Python Web框架Flask信号机制(signals)介绍


Posted in Python onJanuary 01, 2015

信号(signals)

Flask信号(signals, or event hooking)允许特定的发送端通知订阅者发生了什么(既然知道发生了什么,那我们可以知道接下来该做什么了)。

Flask提供了一些信号(核心信号)且其它的扩展提供更多的信号。信号是用于通知订阅者,而不应该鼓励订阅者修改数据。相关信号请查阅文档。

信号依赖于Blinker库。

钩子(hooks)

Flask钩子(通常出现在蓝图或应用程序现存的方法中,比如一些内置装饰器,例如before_request)不需要Blinker库并且允许你改变请求对象(request)或者响应对象(response)。这些改变了应用程序(或者蓝图)的行为。比如before_request()装饰器。

信号看起来和钩子做同样的事情。然而在工作方式上它们存在不同。譬如核心的before_request()处理程序以特定的顺序执行,并且可以在返回响应之前放弃请求。相比之下,所有的信号处理器是无序执行的,并且不修改任何数据。

一般来说,钩子用于改变行为(比如,身份验证或错误处理),而信号用于记录事件(比如记录日志)。

创建信号

因为信号依赖于Blinker库,请确保已经安装。

如果你要在自己的应用中使用信号,你可以直接使用Blinker库。最常见的使用情况是命名一个自定义的Namespace的信号。这也是大多数时候推荐的做法:

from blinker import Namespace

my_signals = Namespace()

现在你可以像这样创建新的信号:

model_saved = my_signals.signal('model-saved')

这里使用唯一的信号名并且简化调试。可以用name属性来访问信号名。

对扩展开发者:

如果你正在编写一个Flask扩展,你想优雅地减少缺少Blinker安装的影响,你可以这样做使用flask.signals.Namespace类。

订阅信号

你可以使用信号的connect()方法来订阅信号。第一个参数是信号发出时要调用的函数,第二个参数是可选的,用于确定信号的发送者。一个信号可以拥有多个订阅者。你可以用disconnect()方法来退订信号。

对于所有的核心Flask信号,发送者都是发出信号的应用。当你订阅一个信号,请确保也提供一个发送者,除非你确实想监听全部应用的信号。这在你开发一个扩展的时候尤其正确。

比如这里有一个用于在单元测试中找出哪个模板被渲染和传入模板的变量的助手上下文管理器:

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)

现在可以轻松地与测试客户端配对:

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

确保订阅使用了一个额外的 **extra 参数,这样当 Flask 对信号引入新参数时你的调用不会失败。

代码中,从 with 块的应用 app 中流出的渲染的所有模板现在会被记录到templates 变量。无论何时模板被渲染,模板对象的上下文中添加上它。

此外,也有一个方便的助手方法(connected_to()) ,它允许你临时地用它自己的上下文管理器把函数订阅到信号。因为上下文管理器的返回值不能按那种方法给定,则需要把列表作为参数传入:

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)

上面的例子看起来像这样:

templates = []

with captured_templates(app, templates, **extra):

    ...

    template, context = templates[0]

发送信号

如果你想要发送信号,你可以通过调用 send() 方法来达到目的。它接受一个发件者作为第一个参数和一些可选的被转发到信号用户的关键字参数:

class Model(object):

    ...
    def save(self):

        model_saved.send(self)

永远尝试选择一个合适的发送者。如果你有一个发出信号的类,把 self 作为发送者。如果你从一个随机的函数发出信号,把current_app._get_current_object()作为发送者。

基于信号订阅的装饰器

在Blinker 1.1中通过使用新的connect_via()装饰器你也能够轻易地订阅信号:

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)

举例

模板渲染

template_rendered信号是Flask核心信号。

当一个模版成功地渲染,这个信号会被发送。这个信号与模板实例 template 和上下文的词典(名为 context )一起调用。

信号发送:

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

订阅示例:
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)


帐号追踪

user_logged_in是Flask-User中定义的信号,当用户成功登入之后,这个信号会被发送。

发送信号:

# Send user_logged_in signal

user_logged_in.send(current_app._get_current_object(), user=user)

下面这个例子追踪登录次数和登录IP:
# 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()

小结

信号可以让你在一瞬间安全地订阅它们。例如,这些临时的订阅对测试很有帮助。使用信号时,不要让信号订阅者(接收者)发生异常,因为异常会造成程序中断。

Python 相关文章推荐
python操作mongodb根据_id查询数据的实现方法
May 20 Python
详解Python中的Cookie模块使用
Jul 06 Python
Python制作简单的网页爬虫
Nov 22 Python
python的pandas工具包,保存.csv文件时不要表头的实例
Jun 14 Python
使用python opencv对目录下图片进行去重的方法
Jan 12 Python
python控制nao机器人身体动作实例详解
Apr 29 Python
Python玩转加密的技巧【推荐】
May 13 Python
Flask框架模板继承实现方法分析
Jul 31 Python
代码总结Python2 和 Python3 字符串的区别
Jan 28 Python
Python和Anaconda和Pycharm安装教程图文详解
Feb 04 Python
python实现求纯色彩图像的边框
Apr 08 Python
Python下opencv库的安装过程及问题汇总
Jun 11 Python
Python模拟登录12306的方法
Dec 30 #Python
python执行shell获取硬件参数写入mysql的方法
Dec 29 #Python
简单的抓取淘宝图片的Python爬虫
Dec 25 #Python
简单使用Python自动生成文章
Dec 25 #Python
Python 抓取动态网页内容方案详解
Dec 25 #Python
利用Psyco提升Python运行速度
Dec 24 #Python
Python解决鸡兔同笼问题的方法
Dec 20 #Python
You might like
php.ini 配置文件的深入解析
2013/06/17 PHP
php+js实现图片的上传、裁剪、预览、提交示例
2013/08/27 PHP
php自定义函数实现汉字转换utf8编码的方法
2016/09/29 PHP
利用phpexcel对数据库数据的导入excel(excel筛选)、导出excel
2017/04/27 PHP
php获取ajax的headers方法与内容实例
2017/12/27 PHP
实例解析php的数据类型
2018/10/24 PHP
JavaScript 常用函数库详解
2009/10/21 Javascript
基于jQuery的淡入淡出可自动切换的幻灯插件
2010/08/24 Javascript
jquery使用淘宝接口跨域查询手机号码归属地实例
2013/11/28 Javascript
javascript中call apply 与 bind方法详解
2016/03/10 Javascript
JQuery的常用选择器、过滤器、方法全面介绍
2016/05/25 Javascript
Jquery遍历select option和添加移除option的实现方法
2016/08/26 Javascript
bootstrap提示标签、提示框实现代码
2016/12/28 Javascript
原生js实现淘宝购物车功能
2020/06/23 Javascript
JS中setTimeout和setInterval的最大延时值详解
2017/02/13 Javascript
JS 组件系列之Bootstrap Table 冻结列功能IE浏览器兼容性问题解决方案
2017/06/30 Javascript
ES6下子组件调用父组件的方法(推荐)
2018/02/23 Javascript
vue移动端微信授权登录插件封装的实例
2018/08/28 Javascript
JavaScript封闭函数及常用内置对象示例
2019/05/13 Javascript
Js Snowflake(雪花算法)生成随机ID的实现方法
2020/08/26 Javascript
[03:05]DOTA2英雄基础教程 嗜血狂魔
2013/12/10 DOTA
Python拼接微信好友头像大图的实现方法
2018/08/01 Python
详解Python odoo中嵌入html简单的分页功能
2019/05/29 Python
用pycharm开发django项目示例代码
2019/06/13 Python
python 检查数据中是否有缺失值,删除缺失值的方式
2019/12/02 Python
python3爬取torrent种子链接实例
2020/01/16 Python
python 安装移动复制第三方库操作
2020/07/13 Python
Pycharm新手使用教程(图文详解)
2020/09/17 Python
python爬虫如何解决图片验证码
2021/02/14 Python
HTML5之HTML元素扩展(上)—新增加的元素及使用概述
2013/01/31 HTML / CSS
欧洲最大的化妆品连锁公司:Douglas道格拉斯
2017/05/06 全球购物
澳大利亚领先的女帽及配饰公司:Morgan&Taylor
2019/12/01 全球购物
学生自我鉴定模板
2013/12/30 职场文书
中医学专业自荐信范文
2014/04/01 职场文书
使用CSS连接数据库的方式
2022/02/28 HTML / CSS
SpringBoot详解整合Redis缓存方法
2022/07/15 Java/Android