zoukankan      html  css  js  c++  java
  • flask框架(三)

    一.请求上下文源码分析

    第一阶段:将ctx(request,session)放到Local对象上
                       
    第二阶段:视图函数导入:request/session 
    request.method
        -LocalProxy对象.method,执行getattr方法,getattr(self._get_current_object(), name)
            -self._get_current_object()返回return self.__local(),self.__local(),在LocakProxy实例化的时候,object.__setattr__(self, '_LocalProxy__local', local),此处local就是:partial(_lookup_req_object, 'request')
    
        -def _lookup_req_object(name):
                top = _request_ctx_stack.top #_request_ctx_stack 就是LocalStack()对象,top方法把ctx取出来
                if top is None:
                    raise RuntimeError(_request_ctx_err_msg)
                return getattr(top, name)#获取ctx中的request或session对象
    
    第三阶段:请求处理完毕
            - 获取session并保存到cookie
            - 将ctx删除

    请求上下文的代码示例

    from  flask import Flask,request
    
    app=Flask(__name__)
    
    @app.route("/")
    def index():
        print(request.form)
        return "1"
    
    if __name__ == '__main__':
        # self,是app,app(),--->Flask对象,Flask的__call__
        # app.__call__  # 源码入口
        app.run()

    源码分析过程

    '''
    1     app.__call__
    2     wsgi_app(environ, start_response) 
    2.1 ctx = self.request_context(environ)
        2.1.1 return RequestContext(self, environ)
            这里的self是app,environ请求相关
        2.1.2 return RequestContext(self, environ)
        得到了RequestContext的对象,而且有request属性
    2.2  2.1中的ctx就是RequestContext的对象  
    
    2.3  ctx.push()执行这个,就是RequestContext的对象的push方法
         2.3.1  #执行这个,self-->ctx
            _request_ctx_stack.push(self) 
            2.3.1.1 我们发现_request_ctx_stack = LocalStack()
            他的push方法的源码:
                    def push(self, obj):
                        rv = getattr(self._local, "stack", None)
                        if rv is None:     
                            # self._local=>stack-->storage['线程id']['stack']=[ctx,]
                            self._local.stack = rv = []
                        rv.append(obj)
                        return rv
                        
    3在请求中获取request.form
    3.1 request是LocalProxy的对象,当获取属性的时候会走__getattr__
        def __getattr__(self, name):
            if name == "__members__":
                return dir(self._get_current_object())
            #name-->form,
            #self._get_current_object()===>ctx.request,form
            #_get_current_object()---》self.__local()
            
            return getattr(self._get_current_object(), name)
            
        3.1.1 self._get_current_object():源码:最终:partial(_lookup_req_object, "request")
         def _get_current_object(self):
         
            if not hasattr(self.__local, "__release_local__"):
                    #local==>partial(_lookup_req_object, "request")
                    #def __init__(self, local, name=None):
                        # object.__setattr__(self, "_LocalProxy__local", local)
                #self.__local()===>local()
                return self.__local()
            try:
                return getattr(self.__local, self.__name__)
            except AttributeError:
                raise RuntimeError("no object bound to %s" % self.__name__)
    4 partial(_lookup_req_object, "request")偏函数的源码
        def _lookup_req_object(name):
            #name是request
            #ctx
            top = _request_ctx_stack.top
            if top is None:
                raise RuntimeError(_request_ctx_err_msg)
            #ctx-->request
            return getattr(top, name)
        4.1中_request_ctx_stack.top
           @property
            def top(self):
            
            try:
                return self._local.stack[-1]
            except (AttributeError, IndexError):
                return None
    '''

    二.蓝图

    蓝图可以对程序进行目录结构划分

    当我们不使用蓝图,自己分文件

    目录结构:

    -templates
    -views
        -__init__.py
        -user.py
        -order.py
    -app.py

    app.py

    from views import app
    if __name__ == '__main__':
        app.run()

    init.py

    from flask import Flask,request
    app = Flask(__name__)
    #不导入这个不行
    from . import account
    from . import order
    from . import user

    user.py

    from . import app
    @app.route('/user')
    def user():
        return 'user'

    order.py

    from . import app
    @app.route('/order')
    def order():
        return 'order'

    使用蓝图之中小型系统

    目录结构:

    -flask_pro
        -flask_test
            -__init__.py
            -static
            -templates
            -views
                -order.py
                -user.py
         -manage.py 

    __init__.py

    from flask import  Flask
    app=Flask(__name__)
    from flask_test.views import user
    from flask_test.views import order
    app.register_blueprint(user.us)
    app.register_blueprint(order.ord)

    manage.py

    from flask_test import  app
    if __name__ == '__main__':
        app.run(port=8008)

    user.py

    from flask import Blueprint
    us=Blueprint('user',__name__)
    
    @us.route('/login')
    def login():
        return 'login'

    order.py

    from flask import Blueprint
    ord=Blueprint('order',__name__)
    
    @ord.route('/test')
    def test():
        return 'order test'

    使用蓝图之大型系统

    目录结构

    -flask_pro
        -flask_test
            -__init__.py
            -admin
                -templates
                -static
                -views
                 -__init__.py
            -web
                -templates
                -static
                -views
                 -__init__.py
         -manage.py

    manage.py

    from pro_flask import app
    
    if __name__ == '__main__':
        app.run()

    __init__.py

    from flask import Flask
    from .admin import admin
    from .web import web
    
    app = Flask(__name__)
    app.debug = True
    
    app.register_blueprint(admin, url_prefix='/admin')
    app.register_blueprint(web)

    -admin.-views.py

    from . import admin
    
    @admin.route('/index')
    def index():
        return 'Admin.Index'

    -admin.-__init__.py

    from flask import Blueprint
    
    admin = Blueprint(
        'admin',
        __name__,
        template_folder='templates',
        static_folder='static'
    )
    from . import views

    -web.-views.py

    from flask import Blueprint
    
    web = Blueprint(
        'web',
        __name__,
        template_folder='templates',
        static_folder='static'
    )
    from . import views

    -web.-__init__.py

    from . import web
    
    @web.route('/index')
    def index():
        return 'Web.Index'

    总结:

    1.xxx = Blueprint('account',name,url_prefix='/xxx'):url_prefix='/xxx'表示的是蓝图URL前缀,在该蓝图下所有url都加前缀

    2.xxx = Blueprint('account', name,url_prefix='/xxx',template_folder='tpls'):给当前蓝图单独使用templates,向上查找,当前找不到,会找总templates

    3.蓝图的before_request,只对当前蓝图有效,如果想要在每个蓝图中加,可以在根目录的app前添加before_request

    4.大型项目,可以模拟出类似于django中app的概念

    三.g对象

    专门用来存储用户信息的g对象,g的全称是global

    g对象存储的信息在一次请求中的所有地方都是可用的,但是后面的请求就不可用了

    g对象和session的区别

    session对象是可以跨request的,只要session还未失效,不同的request的请求会获取到同一个session,但是g对象不是,g对象不需要管过期时间,请求一次就g对象就改变了一次,或者重新赋值了一次

    代码示例

    from flask import Flask, g
    app = Flask(__name__)
    
    def setg():
        g.name = 'sxc'
    
    @app.route('/index')
    def index():
        setg()
        print(g.name)
        return 'ok'
    
    @app.route('/login')
    def login():
        print(g.name)
        return 'login ok'
    
    if __name__ == '__main__':
        app.run()

    四.信号

    Flask框架中的信号基于blinker,主要就是让开发者可以在flask请求过程中定制一些用户行为

    安装:pip3 install blinker

    内置信号

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

    使用信号:

    from flask import Flask,signals,render_template
    
    app = Flask(__name__)
    
    # 往信号中注册函数
    def func(*args,**kwargs):
        print('触发型号',args,kwargs)
    
    # 信号类.内置信号执行时间.connect(绑定的信号)
    signals.request_started.connect(func)
    
    # 触发信号: signals.request_started.send()
    @app.before_first_request
    def before_first1(*args,**kwargs):
        print('我是请求前的第一次')
    
    @app.before_first_request
    def before_first2(*args,**kwargs):
        print('我是请求前')
    
    @app.route('/',methods=['GET',"POST"])
    def index():
        print('视图')
        return 'ok'
    
    
    if __name__ == '__main__':
        # app.wsgi_app
        app.run()

    调用信号是执行:内置信号.send(函数名)

    # 源码分析
    # 这是请求之前第一次执行的函数
    self.try_trigger_before_first_request_functions()
            try:
                request_started.send(self)
                # 这是请求之前执行的函数
                rv = self.preprocess_request()

    分析源码猜想我们的内置信号request_started,是在两个请求扩展的函数之间执行的

    代码验证如下,猜想正确

     一个流程中的信号触发点(了解)

    a. before_first_request
    b. 触发 request_started 信号
    c. before_request
    d. 模板渲染
        渲染前的信号 before_render_template.send(app, template=template, context=context)
            rv = template.render(context) # 模板渲染
        渲染后的信号 template_rendered.send(app, template=template, context=context)
    e. after_request
    f. session.save_session()
    g. 触发 request_finished信号        
        如果上述过程出错:
            触发错误处理信号 got_request_exception.send(self, exception=e)
                
    h. 触发信号 request_tearing_down

    自定义信号

    from flask import Flask, current_app, flash, render_template
    from flask.signals import _signals
    
    app = Flask(import_name=__name__)
    
    # 自定义信号
    xxxxx = _signals.signal('xxxxx')
    
    def func(sender, *args, **kwargs):
        # 第一个参数sender不管是否传都必须接收,否则报错
        print(sender)
    
    # 自定义信号中注册函数
    xxxxx.connect(func)
    
    @app.route("/x")
    def index():
        # 触发信号
        # 第一个参数sender可以使用位置传参,后面的参数都只能使用关键字传参
        xxxxx.send('123123', k1='v1')
        return 'Index'
    
    if __name__ == '__main__':
        app.run()

    注意:

      1.自定义信号的函数定义时第一个参数sender不管是否传都必须接收,否则报错

      2.在触发信号xxxxx.send('123123', k1='v1')时,第一个参数sender可以使用位置传参,后面的参数都只能使用关键字传参

    五.flask_session

    作用:将默认保存的签名cookie中的值 保存到 redis/memcached/file/Mongodb/SQLAlchemy

    安装:pip3 install flask-session

    第一种使用方式

    from flask import Flask,session
    from flask_session import RedisSessionInterface
    import redis
    app = Flask(__name__)
    conn=redis.Redis(host='127.0.0.1',port=6379)
    # key_prefix='sxc'是session存储的前缀
    # use_signer是否对key签名
    app.secret_key = 'sxfafasf'
    # permanent=True表示是否关闭浏览器清除cookie
    app.session_interface=RedisSessionInterface(conn,key_prefix='sxc', use_signer=True,  permanent=False)
    @app.route('/')
    def hello_world():
        session['name']='sxc'
        return 'Hello World!'
    
    @app.route('/index')
    def index():
        print(session['name'])
        return 'index World!'
    
    if __name__ == '__main__':
        app.run()

    第二种使用方式

    from redis import Redis
    from flask_session import Session
    from flask import Flask,session
    app = Flask(__name__)
    app.config['SESSION_TYPE'] = 'redis'
    app.config['SESSION_REDIS'] = Redis(host='127.0.0.1',port='6379')
    app.config['SESSION_KEY_PREFIX'] = 'sxc'  # 设置session_key的前缀
    app.config['SESSION_USE_SIGNER'] = True  # 是否对key签名
    app.config['SESSION_PERMANENT'] = False  # 表示是否关闭浏览器清除cookie
    
    app.secret_key = 'sadgweqw'
    Session(app)
    
    @app.route('/')
    def hello_world():
        session['name']='sxc'
        return 'Hello World!'
    
    @app.route('/index')
    def index():
        print(session['name'])
        return 'index World!'
    
    if __name__ == '__main__':
        app.run()

    问题:设置cookie时,如何设定关闭浏览器则cookie失效。

    response.set_cookie('k','v',exipre=None)#这样设置即可
    #在session中设置
    app.session_interface=RedisSessionInterface(conn,key_prefix='lqz',permanent=False)
    #一般不用,我们一般都设置超时时间,多长时间后失效

    问题:cookie默认超时时间是多少?如何设置超时时间

    #源码expires = self.get_expiration_time(app, session)
    'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),#这个配置文件控制

     91

  • 相关阅读:
    ubuntu下使用ppa安装codeblocks集成开发环境
    CentOS g++ 安装
    CentOS 7.1云服务器 配置FTP服务器vsftpd
    div高度自适应浏览器高度
    HTML中的head结构
    javascript高级进阶系列
    javascript集中跨域方法
    canvas标签的width和height以及style.width和style.height的区别
    html5学习笔记之入新特性
    ie下a标签里的图片会有边框
  • 原文地址:https://www.cnblogs.com/sxchen/p/11854250.html
Copyright © 2011-2022 走看看