Flask - 蓝图 | 请求上下文 | g对象 | flask-session | 信号

蓝图 

对程序进行目录结构划分

  • 蓝图之中小型系统 

项目结构百度盘  2l7r

目录结构:

├── flasks
    ├── pro_flask        # 项目文件夹(包)        
        ├── statics      # 图片等文件
        ├── templates    # 模板文件夹
        ├── views        # 视图函数文件夹
        └── __init__.py  
    └── manage.py        # 启动文件
flasks/manage.py
from pro_flask import app

if __name__ == '__main__':
    app.run()
pro_flask/__init__.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask

app = Flask(__name__,template_folder='templates',static_folder='statics',static_url_path='/static')

@app.before_request
def before():
    print("我是app里面的befor_request")  
# 可当做是全局配置的函数,每个请求来都会执行该函数

# 将视图中的蓝图对象导入并注册
from .views.account import account

app.register_blueprint(account)
pro_flask/views/account.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Blueprint  # 导入蓝图
from flask import render_template
from flask import request
# 实例化蓝图
account = Blueprint('acc', __name__)

# 蓝图对象配置路由
@account.route('/login.html', methods=['GET', "POST"])
def login():
    print(request.form)
    return render_template('login.html')
pro_flask/templates/login.html
<body>

<h1>用户登录</h1>
<form method="POST">
    <input type="text" name="user"/>
    <input type="submit" value="提交"/>
</form>

<img src="/static/code.png">

<img src="{{ url_for('static',filename='code.png') }}">

</body>
  • 蓝图之大型系统

项目结构百度盘  mhs5

总结:

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

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

3 蓝图的befort_request,对当前蓝图有效

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

请求上下文源码分析

第一阶段:将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删除

 

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
View Code

 

g对象

  • g对象的特性

当前请求内你设置就可以取,必须先设置,后取,当前请求可以取无限次
就算你当前的请求,设置了,如果不取,其他请求过来,也取不到

from flask import Flask, g, url_for, redirect

app = Flask(__name__)

@app.before_request
def after1():
    g.name = 'aaa'

@app.route('/test')
def test():
    print(g.name)  # 可以取
    return redirect(url_for('login'))

@app.route('/login')
def login():
    print(g.name)  # 也可以取,因为g对象是在每个请求来都设置了
    return 'login'

# g对象的取值是针对一次请求来定的
  • g对象和session的区别

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

 

flask-session

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

  • 安装

>: pip3 install flask-session
  • 使用

from flask import Flask,session
import  redis
from flask_session import Session
app = Flask(__name__)
app.config['SESSION_TYPE'] = 'redis'  # 连接redis
app.config['SESSION_REDIS'] =redis.Redis(host='127.0.0.1',port='6379')
app.config['SESSION_KEY_PREFIX']="session_prefix"
Session(app)

@app.route('/')
def hello_world():
    session['key']='value'
    return 'Hello World!'

@app.route("/index")
def index():
    print(session['key'])
    return "ok"

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),#这个配置文件控制

 

信号

  • 安装

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__)

# 往信号中注册函数
#1给信号绑定要执行的函数
#无需管调用,因为flask,已经给我们设置调用点
def func(*args,**kwargs):
    print('触发型号',args,kwargs)
#与该信号进行绑定
signals.request_started.connect(func)
# signals.request_started.send
# 触发信号: signals.request_started.send()
@app.before_first_request
def before_first1(*args,**kwargs):
    print("befor_first_request")


@app.before_request
def before_first3(*args,**kwargs):
    print("befor_request")

@app.route('/',methods=['GET',"POST"])
def index():
    print('视图')
    return "视图"


if __name__ == '__main__':
  # app.wsgi_app
    app.run()
  • 自定义信号

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):
    print(sender)
# 自定义信号中注册函数
xxxxx.connect(func)
@app.route("/x")
def index():
    # 触发信号
    xxxxx.send('123123', k1='v1')
    return 'Index' 
 
if __name__ == '__main__':
    app.run()

 

posted @ 2019-11-13 20:10  waller  阅读(228)  评论(0)    收藏  举报