flask第四篇

一、flask上下文源码分析(详见txt和图片)

1、具体流程以及源码分析

请求上下文执行流程(ctx):
    1 flask项目一启动,有6个全局变量
    # context locals
	_request_ctx_stack = LocalStack()       # LocalStack对象
	_app_ctx_stack = LocalStack()			# LocalStack对象
	current_app = LocalProxy(_find_app)     # LocalProxy对象
	request = LocalProxy(partial(_lookup_req_object, "request"))   # LocalProxy对象
	session = LocalProxy(partial(_lookup_req_object, "session"))   #  LocalProxy对象
	g = LocalProxy(partial(_lookup_app_object, "g"))  # LocalProxy对象
    2 请求来了 app()执行,调用Flask类的__call__()方法,内部执行了return self.wsgi_app(environ, start_response)
    3 进入wsgi_app()
    	3.1 执行ctx = self.request_context(environ),返回RequestContext对象
        def request_context(self,environ):
            return RequestContext(self,environ)
        进入RequestContext对象中
        Class RequestContext(object):
            def __init__(self,app,environ,request=None,session=None):
            	```
              	if request is None:
                    request = app.request_class(environ)
                self.request = request
        		self.session = session
       	可以看出封装了当次请求的request对象以及session等
        3.2 执行ctx.push()-->RequestContext对象的push方法
        	其中_request_ctx_stack.push(self)中的self是ctx对象
            根据全局变量_request_ctx_stack = LocalStack(),进入LocalStack类中找push方法
        def push(self, obj):  
        """Pushes a new item to the stack"""
       		rv = getattr(self._local, "stack", None)  # 通过反射找self._local,取出"stack"属性,没有返回None;self_local是在__init__实例化时候生成的self._local = Local();其中 Local()是flask中封装了支持线程和协程的local对象,加()调用 
        	if rv is None:   # 一开始取不到stack,返回None
            	self._local.stack = rv = []
        	rv.append(obj)  # 把ctx对象加入到列表中,storage可以写成{'线程id1':{'stack':[ctx,]},{'线程id2':{'stack':[ctx,]}},...}
        	return rv
	4 若在视图视图函数中使用request对象,比如print(request)
    	4.1 根据全局变量request = LocalProxy(partial(_lookup_req_object, "request"))可知,request对象的类是LocalProxy,并且使用了偏函数,提前传了request参数。request对象调用类的__repr__方法,obj = self._get_current_object(),执行self._get_current_object()方法,源码如下
        def _get_current_object(self):
        	if not hasattr(self.__local, "__release_local__"):  #反射,self.__local是在__init__实例化中生成,object.__setattr__(self, "_LocalProxy__local", local)使用了隐藏属性_类__属性-->self._LocalProxy__local = local
            	return self.__local()  # 很显然没反射到,加()执行了偏函数_lookup_req_object方法
        	try:
            	return getattr(self.__local, self.__name__)
        	except AttributeError:
            	raise RuntimeError("no object bound to %s" % self.__name__)
       	4.2 偏函数源码分析 
		def _lookup_req_object(name):  # 提前传值request
    		top = _request_ctx_stack.top   # 调用LocalStack类中的top方法,因为在一个线程内,返回的就是当次请求的ctx对象
    		if top is None:
        		raise RuntimeError(_request_ctx_err_msg)
    		return getattr(top, name)  # 通过反射,取出的就是当次请求的request对象
        # top源码
        def top(self)
        try:
            return self._local.stack[-1]   # 返回当前最后一个ctx对象
        except (AttributeError, IndexError):
            return None
        4.3 综上所述,print(request)就是打印当次请求的request对象的__str__;同理,如果在函数视图中print(request.method)实际就是打印出当次请求request的method属性
	5 最后执行ctx.auto_pop(error),将ctx对象移除

2、关于session以及请求扩展分析

# session分析
1 请求来了app对象类的open_session----> 执行wsgi_app()-->ctx = self.request_context(environ),执行ctx.push()---->
        if self.session is None:
            session_interface = self.app.session_interface  # self是ctx对象,ctx中有个app就是flask对象,session_interface = SecureCookieSessionInterface()
            self.session = session_interface.open_session(self.app, self.request)

            if self.session is None:  # 经过上面还是None的话,生成了个空session
                self.session = session_interface.make_null_session(self.app) 
2 请求走了app对象类的save_session----->-response = self.full_dispatch_request() 方法内部:执行了before_first_request,before_request,视图函数,after_request,savesession
self.full_dispatch_request()---->执行:self.finalize_request(rv)--->self.process_response(response)---->最后:self.session_interface.save_session(self, ctx.session, response)
# 请求扩展相关
before_first_request,before_request,after_request依次执行
# flask有个请求上下文,应用上下文
-ctx:
	-是:RequestContext对象:封装了request和session
	-调用了:_request_ctx_stack.push(self)就是把:ctx放到了storage字典中
-app_ctx:
	-是:AppContext(self) 对象:封装了当前的app和g
	-调用 _app_ctx_stack.push(self) 就是把:app_ctx放到了storage字典中
# g是个什么?
专门用来存储用户信息的g对象,g的全称的为global 
g对象在一次请求中的所有的代码的地方,都是可以使用的 (当次请求中传递一些数据)
# g对象和session的区别
g对象只对当次请求有效(当此请求内有效)
session:可以跨请求,该用户的多次请求中都可以使用
# 代理模式
request和session就是代理对象,用的就是代理模式。本质是LocalProxy对象,代理到了request和session对象上。

二、flask-session的使用

1 由于原生的flask中session是加密后放到了cookie中
2 我们想保存到文件中,数据库中,redis(比较多)。。。
3 借助于第三方:flask-session
# 第一种方式
from flask import Flask,session
import redis
from flask_session import RedisSessionInterface
app = Flask(__name__)

conn = redis.Redis(host='127.0.0.1',port=6379)
app.session_interface = RedisSessionInterface(conn,key_prefix='lqz')     # session_interface = SecureCookieSessionInterface()原生session是加密放在cookie中的
@app.route('/set_session')
def set_session():
    session['age']='123'
    return 'session写入了,写了age=123'
@app.route('/get_session')
def get_session():
    s = session.get('age','取不到')
    return '获取到的session是'+s
if __name__ == '__main__':
    app.run()
# 第二种使用app.config
from flask import Flask,session
from flask_session import Session
from redis import Redis
from datetime import timedelta
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'] = 'lqz'
# 设置session的过期时间,默认是31天
app.config['PERMANENT_SESSION_LIFETIME']=timedelta(days=31) 
app.config.from_object('settings.Pro')
Session(app)  
@app.route('/set_session')
def set_session():
    session['name']='lqz'
    return 'session写入了,写了name=lqz'
@app.route('/get_session')
def get_session():
    s = session.get('name','取不到')
    return '获取到的session是'+s
if __name__ == '__main__':
    app.run()
# 第三种是在建配置文件settings.py
from flask import Flask,session
from flask_session import 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'] = 'lqz'
# # 设置session的过期时间
# app.config['PERMANENT_SESSION_LIFETIME']=timedelta(days=31)
app.config.from_object('settings.Pro')
Session(app)   # 本质跟上面一样,只不过通过配置文件来处理,好处是后期只改配置文件,即可完成配置
@app.route('/set_session')
def set_session():
    session['name']='lqz'
    return 'session写入了,写了name=lqz'
@app.route('/get_session')
def get_session():
    s = session.get('name','取不到')
    return '获取到的session是'+s
if __name__ == '__main__':
    app.run()
# settings.py中
from redis import Redis
from datetime import timedelta
class Pro:
    DEBUG = True
    SESSION_TYPE = 'redis'
    SESSION_REDIS = Redis(host='127.0.0.1',port='6379')
    SESSION_KEY_PREFIX = 'lqz'
    PERMANENT_SESSION_LIFETIME = timedelta(days=31)

三、数据库连接池

https://www.cnblogs.com/liuqingzheng/articles/9006055.html
    
1 传统方案存在的问题
# 第一种方案,全局使用沟通一个curser会存在效率问题,安全性问题
conn = pymysql.Connect(host='127.0.0.1', user='root', password="123", database='luffy', port=3306)
curser = conn.cursor()
# 函数
# 第二种:不放在全局,则不限制数据库的连接数,会导致连接数暴增
# 函数
conn = pymysql.Connect(host='127.0.0.1', user='root', password="123", database='luffy', port=3306)
curser = conn.cursor()

2 使用数据库连接池
	-pip3 install DButils
    -两种模式:
    	第一种模式不用(为每个线程创建一个连接,线程即使调用了close方法,也不会关闭,只是把连接重新放到连接池,供自己线程再次使用。当线程终止时,连接自动关闭)
        第二种:创建一批连接到连接池,供所有线程共享使用,使用单例模式,pool就是单例

3 使用步骤
	-第一步:新建sql_pool.py
    import pymysql
    # from DBUtils.PooledDB import PooledDB
    from dbutils.pooled_db import PooledDB
    POOL = PooledDB(
        creator=pymysql,  # 使用链接数据库的模块
        maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
        mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
        maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
        maxshared=3,  # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
        blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
        maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
        setsession=[],  # 开始会话前执行的命令列表。
        ping=0,
        # ping MySQL服务端,检查是否服务可用。
        host='127.0.0.1',
        port=3306,
        user='root',
        password='123',
        database='luffy',
        charset='utf8'
    )
    
    -第二步:使用
    from sql_pool import POOL
	conn = POOL.connection() # 从连接池种取一个链接(如果没有,阻塞在这)
    curser = conn.cursor()
    curser.execute('select * from luffy_order where id<2')
    res=curser.fetchall()
    print(res)

四、flask-script

1 django 运行项目  python manage.py runserver 
2 借助于flask-script可以子定制命令
	pip3 install flask-script
    
3 使用
	-自带一个runserver
    -自定制命令

from flask import Flask
from flask_script import Manager
app = Flask(__name__)
manager=Manager(app)

# 自己定制命令
@manager.command
def custom(arg):
    """
    自定义命令
    python manage.py custom 123
    :param arg:
    :return:
    """
    print(arg)

@manager.option('-n', '--name', dest='name')
@manager.option('-u', '--url', dest='url')
def cmd(name, url):
    """
    自定义命令(-n也可以写成--name)
    执行: python manage.py  cmd -n lqz -u http://www.oldboyedu.com
    执行: python manage.py  cmd --name lqz --url http://www.oldboyedu.com
    :param name:
    :param url:
    :return:
    """
    print(name, url)
#有什么用?
#把excel的数据导入数据库,定制个命令,去执行

@app.route('/')
def index():
    return '首页'

if __name__ == '__main__':
    manager.run()
#以后在执行,直接:python3 运行的文件名.py runserver
#python3 manage.py runserver --help
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值