flask---信号

一、flask-信号

   Flask框架中的信号基于blinker

flask-信号的安装

   pip3 install blinker

10个内置信号

1. 内置信号
            10个信号:
                2. request_started = _signals.signal('request-started')                # 请求到来前执行
                5. request_finished = _signals.signal('request-finished')              # 请求结束后执行
                 
                3. before_render_template = _signals.signal('before-render-template')  # 模板渲染前执行
                4. template_rendered = _signals.signal('template-rendered')            # 模板渲染后执行
                 
                2/3/4/5或不执行 got_request_exception = _signals.signal('got-request-exception')    # 请求执行出现异常时执行
                 
                6. request_tearing_down = _signals.signal('request-tearing-down')      # 请求执行完毕后自动执行(无论成功与否)
                7. appcontext_tearing_down = _signals.signal('appcontext-tearing-down')# 请求上下文执行完毕后自动执行(无论成功与否)
                 
                 
                1. appcontext_pushed = _signals.signal('appcontext-pushed')            # 请求app上下文push时执行
                
                8. appcontext_popped = _signals.signal('appcontext-popped')            # 请求上下文pop时执行
                
                message_flashed = _signals.signal('message-flashed')                   # 调用flask在其中添加数据时,自动触发

二、信号源码这整个流程源码

 def wsgi_app(self, environ, start_response):
       
        ctx = self.request_context(environ)    #实例化context这对象
        ctx.push()
        error = None
        try:
            try:
                #执行:before装饰器函数,视图函数,after装饰器函数
                response = self.full_dispatch_request()
            except Exception as e:    #(6.请求执行出现异常是执行got_request_exception)
                error = e
                response = self.handle_exception(e)
            except:
                error = sys.exc_info()[1]
                raise
            #将处理的内容返回给浏览器
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            #结束
            ctx.auto_pop(error)  #(ctx是requestContext的对象,执行ctx的pop方法)
def wsgi_app
def push(self):
      
        app_ctx = _app_ctx_stack.top
        if app_ctx is None or app_ctx.app != self.app:
           #app_ctx=AppContext(self.app)  (实例化做完开始执行 app_ctx.push()中的app_context中的push) 
            app_ctx = self.app.app_context()   #把app放到队列放到Local中
           #触发执行appcontext_push的方法(1.第一个信号已经触发被执行)
            app_ctx.push()

            self._implicit_app_ctx_stack.append(app_ctx) #(app_ctx是AppContext创建的对象)
        else:
            self._implicit_app_ctx_stack.append(None)

        if hasattr(sys, 'exc_clear'):
            sys.exc_clear()

     #将RequestContext对象,添加到Local中

        _request_ctx_stack.push(self)

        # Open the session at the moment that the request context is
        # available. This allows a custom open_session method to use the
        # request context (e.g. code that access database information
        # stored on `g` instead of the appcontext).
        self.session = self.app.open_session(self.request)
        if self.session is None:
            self.session = self.app.make_null_session()
def push(
 def full_dispatch_request(self):
       #执行:@before_first_request装饰的所有函数
        self.try_trigger_before_first_request_functions()
        try:
            request_started.send(self)   #(2.视图函数没执行,请求刚进来执行 request_started.send(self))
            rv = self.preprocess_request()
            if rv is None:
                 #执行视图函数
                 #执行session
                rv = self.dispatch_request()    #(执行视图函数)
                                                #如果有模板先执行
                                                #     3. before_render_template = _signals.signal('before-render-template')  # 模板渲染前执行
                                                #     4. template_rendered = _signals.signal('template-rendered')            # 模板渲染后执行
                                                #执行完这两个模板后在执行下面的
                
        except Exception as e:
            rv = self.handle_user_exception(e)
        #执行:@after_request装饰所有的函数,保存session
        return self.finalize_request(rv)
def full_dispatch_request
 def process_response(self, response):
        
            ctx = _request_ctx_stack.top
            bp = ctx.request.blueprint
            funcs = ctx._after_request_functions
            if bp is not None and bp in self.after_request_funcs:
                funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
            if None in self.after_request_funcs:
                funcs = chain(funcs, reversed(self.after_request_funcs[None]))
            #执行 @after_request装饰的所有的函数
            for handler in funcs:
                response = handler(response)
            #最后处理session
            if not self.session_interface.is_null_session(ctx.session):
                self.save_session(ctx.session, response)
            return response
def process_response
def finalize_request(self, rv, from_error_handler=False):
        response = self.make_response(rv)
        try:
            response = self.process_response(response)
            request_finished.send(self, response=response)  #(5.视图函数执行完,请求结束后执行request_finished.send)
        except Exception:
            if not from_error_handler:
                raise
            self.logger.exception('Request finalizing failed with an '
                                  'error while handling an error')
        return response
def finalize_request
def auto_pop(self, exc):
        if self.request.environ.get('flask._preserve_context') or \
           (exc is not None and self.app.preserve_context_on_exception):
            self.preserved = True
            self._preserved_exc = exc
        else:
            self.pop(exc)
def auto_pop
def pop(self, exc=_sentinel):
        app_ctx = self._implicit_app_ctx_stack.pop()   (#app_ctx是AppContext(self.app)对象))

        try:
            clear_request = False
            if not self._implicit_app_ctx_stack:
                self.preserved = False
                self._preserved_exc = None
                if exc is _sentinel:
                    exc = sys.exc_info()[1]
                self.app.do_teardown_request(exc)

                # If this interpreter supports clearing the exception information
                # we do that now.  This will only go into effect on Python 2.x,
                # on 3.x it disappears automatically at the end of the exception
                # stack.
                if hasattr(sys, 'exc_clear'):
                    sys.exc_clear()

                request_close = getattr(self.request, 'close', None)
                if request_close is not None:
                    request_close()
                clear_request = True
        finally:
            rv = _request_ctx_stack.pop()

            # get rid of circular dependencies at the end of the request
            # so that we don't require the GC to be active.
            if clear_request:
                rv.request.environ['werkzeug.request'] = None

            # Get rid of the app as well if necessary.
            if app_ctx is not None:
                app_ctx.pop(exc)

            assert rv is self, 'Popped wrong request context.  ' \
                '(%r instead of %r)' % (rv, self)
def pop
 def do_teardown_request(self, exc=_sentinel):
        if exc is _sentinel:
            exc = sys.exc_info()[1]
        funcs = reversed(self.teardown_request_funcs.get(None, ()))
        bp = _request_ctx_stack.top.request.blueprint
        if bp is not None and bp in self.teardown_request_funcs:
            funcs = chain(funcs, reversed(self.teardown_request_funcs[bp]))
        for func in funcs:
            func(exc)
        request_tearing_down.send(self, exc=exc)     #( 6.request_tearing_down请求执行完毕后自动执行(无论成功与否))
def do_teardown_request
def do_teardown_appcontext(self, exc=_sentinel):
       
        if exc is _sentinel:
            exc = sys.exc_info()[1]
        for func in reversed(self.teardown_appcontext_funcs):
            func(exc)
        appcontext_tearing_down.send(self, exc=exc)    (#7.appcontext_tearing_down请求上下文执行完毕后自动执行(无论成功与否))
        
def do_teardown_appcontext
def pop(self, exc=_sentinel):
        try:
            self._refcnt -= 1
            if self._refcnt <= 0:
                if exc is _sentinel:
                    exc = sys.exc_info()[1]
                self.app.do_teardown_appcontext(exc)
        finally:
            rv = _app_ctx_stack.pop()
        assert rv is self, 'Popped wrong app context.  (%r instead of %r)' \
            % (rv, self)
        appcontext_popped.send(self.app)   (#8. appcontext_popped # 请求上下文pop时执行)
def pop

三、自定义信号

 

from flask import Flask,flash
from flask.signals import _signals
app = Flask(__name__)

wh = _signals.signal('wh')


# 定义函数
def luominwen(*args,**kwargs):
print('罗姑娘',args,kwargs)

# 定义函数
def shaowei(*args,**kwargs):
print('少姑娘',args,kwargs)

# 将函数注册到request_started信号中: 添加到这个列表
wh.connect(luominwen)
wh.connect(shaowei)

 

@app.route('/index')
def index():
# 触发这个信号:执行注册到列表中的所有函数
# 发送短信,邮件,微信
wh.send(sender='xxx',a1=123,a2=456)
return "xx"

if __name__ == '__main__':
app.__call__
app.run()

 

问题:
特殊的装饰器和信号有什么区别?
- 装饰器返回值有意义
- 信号用于做什么呢?
- 降低代码之间的耦合

 四、django内置的信号

 

Request/response signals
                    request_started             # 请求到来前,自动触发
                    request_finished            # 请求结束后,自动触发
                    got_request_exception       # 请求异常后,自动触发
                
                Model signals
                    pre_init                    # django的modal执行其构造方法前,自动触发
                    post_init                   # django的modal执行其构造方法后,自动触发
                    
                    pre_save                    # django的modal对象保存前,自动触发
                    post_save                   # django的modal对象保存后,自动触发
                    
                    pre_delete                  # django的modal对象删除前,自动触发
                    post_delete                 # django的modal对象删除后,自动触发
                    
                    m2m_changed                 # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
                    
                    class_prepared              # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
                    
                Management signals
                    pre_migrate                 # 执行migrate命令前,自动触发
                    post_migrate                # 执行migrate命令后,自动触发
                
                Test signals
                    setting_changed             # 使用test测试修改配置文件时,自动触发
                    template_rendered           # 使用test测试渲染模板时,自动触发
                Database Wrappers
                    connection_created          # 创建数据库连接时,自动触发

 

需求:新浪面试,数据库12张表,每张表创建一条数据时,记录一条日志。
                   重写save方法或者用信号,找到信号注册一个函数
                   注意注册一个函数是:把这个注册的函数放在文件刚启动起来的位置,如:__init.py 里面
                   注册的函数为:

def func(*args,**):
    print(args,kwargs)
post_save.connect(func)                    
v1 = [11,22,33,44]
v2 = [1,4,7,5]
from itertools import chain
ff = []
for i in chain(v1,v2):   #chain会把两个列表连接在一块
    ff.append(i)
print(ff)     #[11, 22, 33, 44, 1, 4, 7, 5]
chain的应用

flask信号本生自己没有,用的是别人的,并且这些信号通过装饰器全部可以代替了的,但是Django里面有些特殊的
就是那些model操作根本没有装饰器,就是同过内置的信号来完成的

 

 

posted @ 2018-01-10 16:52  孟庆健  阅读(266)  评论(0编辑  收藏  举报