Flask - 进阶二
flask上下文管理
内容
-
django和flask的区别
- 概括的区别 - django中提供功能列举 - 请求处理机制不同,django是通过传参的形式,flask是通过上下文管理的方式实现。
-
wsgi
django和flask内部都没有实现socket,而是wsgi实现。 wsgi是web服务网管接口,他是一个协议,实现它的协议的有:wsgiref/werkzurg/uwsgi
# django之前 from wsgiref.simple_server import make_server def run(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ] if __name__ == '__main__': httpd = make_server('127.0.0.1', 8000, run) httpd.serve_forever()
# flask之前 from werkzeug.serving import run_simple from werkzeug.wrappers import BaseResponse def func(environ, start_response): print('请求来了') response = BaseResponse('你好') return response(environ, start_response) if __name__ == '__main__': run_simple('127.0.0.1', 5000, func)
-
web框架都有的功能:路由、视图、模板
-
before_request/after_request
相当于django的中间件,对所有的请求定制功能。
-
tempalte_global / template_filter
定制在所有模板中都可以使用函数
-
路由系统处理本质 @app.route
将url和函数打包成rule,天剑到map对象,map再放到app中。
-
路由
- 装饰器实现 / add_url_rule
- endpoint
- 动态路由
- 如果给视图加装饰器:放route下面 、 functools
-
视图
- FBV
- CBV(返回一个view函数,闭包的应用场景)
- 应用到的功能都是通过导入方式:request/session
-
flask中支持session
默认将session加密,然后保存在浏览器的cookie中。
-
模板比django方便一点
支持python原生的语法
-
蓝图
帮助我们可以对很多的业务功能做拆分,创建多个py文件,把各个功能放置到各个蓝图,最后再将蓝图注册到flask对象中。 帮助我们做目录结构的拆分。
-
threading.local对象
自动为每个线程开辟空间,让你进行存取值。
-
数据库链接池 DBUtils (SQLHelper)
-
面向对象上下文管理(with)
概要
- flask上下文源码
- flask的扩展
详细
1. 栈
后进先出,通过列表可以实现一个栈。
v = [11,22,33]
v.append(44)
v.pop()
应用场景:
- 节流
2. 面向对象
class Foo(object):
def __setattr__(self, key, value):
print(key,value)
def __getattr__(self, item):
print(item)
obj = Foo()
obj.x = 123
obj.x
-
drf中request
request.data request.query_params request._request request._request.POST request._request.GET # request.data request.query_params request.POST request.Data
class Local(object):
def __init__(self):
# self.storage = {}
object.__setattr__(self,"storage",{})
def __setattr__(self, key, value):
self.storage[key] = value
def __getattr__(self, item):
return self.storage.get(item)
local = Local()
local.x1 = 123
print(local.x1)
3.线程唯一标识
import threading
from threading import get_ident
def task():
ident = get_ident()
print(ident)
for i in range(20):
t = threading.Thread(target=task)
t.start()
4.自定义threading.local
import threading
"""
storage = {
1111:{'x1':[0,1,2,3]},
1112:{'x1':1}
1113:{'x1':2}
1114:{'x1':3}
1115:{'x1':4}
}
"""
class Local(object):
def __init__(self):
object.__setattr__(self,'storage',{})
def __setattr__(self, key, value):
ident = threading.get_ident()
if ident in self.storage:
self.storage[ident][key] = value
else:
self.storage[ident] = {key:value}
def __getattr__(self, item):
ident = threading.get_ident()
if ident not in self.storage:
return
return self.storage[ident].get(item)
local = Local()
def task(arg):
local.x1 = arg
print(local.x1)
for i in range(5):
t = threading.Thread(target=task,args=(i,))
t.start()
5.加强版threading.local
import threading
"""
storage = {
1111:{'x1':[]},
1112:{'x1':[]}
1113:{'x1':[]}
1114:{'x1':[]}
1115:{'x1':[]},
1116:{'x1':[]}
}
"""
class Local(object):
def __init__(self):
object.__setattr__(self,'storage',{})
def __setattr__(self, key, value):
ident = threading.get_ident()
if ident in self.storage:
self.storage[ident][key].append(value)
else:
self.storage[ident] = {key:[value,]}
def __getattr__(self, item):
ident = threading.get_ident()
if ident not in self.storage:
return
return self.storage[ident][item][-1]
local = Local()
def task(arg):
local.x1 = arg
print(local.x1)
for i in range(5):
t = threading.Thread(target=task,args=(i,))
t.start()
6.flask源码关于local的实现
try:
# 协程
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident
"""
__storage__ = {
1111:{"stack":[汪洋] }
}
"""
class Local(object):
def __init__(self):
# self.__storage__ = {}
# self.__ident_func__ = get_ident
object.__setattr__(self, "__storage__", {})
object.__setattr__(self, "__ident_func__", get_ident)
def __iter__(self):
return iter(self.__storage__.items())
def __release_local__(self):
self.__storage__.pop(self.__ident_func__(), None)
def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
ident = self.__ident_func__() # 1111
storage = self.__storage__
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value}
def __delattr__(self, name):
try:
del self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)
class LocalStack(object):
def __init__(self):
self._local = Local()
def push(self, obj):
"""Pushes a new item to the stack"""
# self._local.stack == getattr
# rv = None
rv = getattr(self._local, "stack", None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
return rv
def pop(self):
stack = getattr(self._local, "stack", None)
if stack is None:
return None
elif len(stack) == 1:
# release_local(self._local)
# del __storage__[1111]
return stack[-1]
else:
return stack.pop()
@property
def top(self):
try:
return self._local.stack[-1]
except (AttributeError, IndexError):
return None
obj = LocalStack()
obj.push('汪洋')
obj.push('成说')
print(obj.top)
obj.pop()
obj.pop()
总结:
在flask中有个local类,他和threading.local的功能一样,为每个线程开辟空间进行存取数据,他们两个的内部实现机制,内部维护一个字典,以线程(协程)ID为key,进行数据隔离,如:
__storage__ = {
1211:{'k1':123}
}
obj = Local()
obj.k1 = 123
在flask中还有一个LocalStack的类,他内部会依赖local对象,local对象负责存储数据,localstack对象用于将local中的值维护成一个栈。
__storage__ = {
1211:{'stack':['k1',]}
}
obj= LocalStack()
obj.push('k1')
obj.top
obj.pop()
7.flask源码中总共有2个localstack对象
# context locals
__storage__ = {
1111:{'stack':[RequestContext(reqeust,session),]},
1123:{'stack':[RequestContext(reqeust,session),]},
}
_request_ctx_stack = LocalStack()
__storage__ = {
1111:{'stack':[AppContenxt(app,g),]}
1123:{'stack':[AppContenxt(app,g),]},
}
_app_ctx_stack = LocalStack()
_request_ctx_stack.push('小魔方')
_app_ctx_stack.push('大魔方')
- 上下文管理
- 请求上下文管理
- 应用上下文管理
7.源码初识
7.1 项目启动
-
实例化Flask对象
app = Flask(__name__)
1. 对app对象封装一些初始化的值。 app.static_url_path app.static_folder app.template_folder app.view_functions = {} 2. 添加静态文件的路由 self.add_url_rule( self.static_url_path + "/<path:filename>", endpoint="static", host=static_host, view_func=self.send_static_file, ) 3. 实例化了url_map的对象,以后在map对象中放 【/index/ 函数的对象应观】 class Flask(object): url_rule_class = Rule url_map_class = Map def __init__(self...): self.static_url_path self.static_folder self.template_folder self.view_functions = {} self.url_map = self.url_map_class() app = Flask() app.view_functions app.url_rule_class
-
加载配置文件(给app的config进行赋值)
from flask import Flask app = Flask(__name__,static_url_path='/xx') app.config.from_object('xx.xx')
1. 读取配置文件中的所有键值对,并将键值对全都放到Config对象。(Config是一个字典) 2. 把包含所有配置文件的Config对象,赋值给 app.config
-
添加路由映射
from flask import Flask app = Flask(__name__,static_url_path='/xx') @app.route('/index') def index(): return 'hello world'
1. 将 url = /index 和 methods = [GET,POST] 和 endpoint = "index"封装到Rule对象 2. 将Rule对象添加到 app.url_map中。 3. 把endpoint和函数的对应关系放到 app.view_functions中。
-
截止目前
app.config app.url_map app.view_functions
-
运行flask
from flask import Flask app = Flask(__name__,static_url_path='/xx') @app.route('/index') def index(): return 'hello world' if __name__ == '__main__': app.run()
1. 内部调用werkzeug的run_simple,内部创建socket,监听IP和端口,等待用户请求到来。 2. 一旦有用户请求,执行app.__call__方法。 class Flask(object): def __call__(self,envion,start_response): pass def run(self): run_simple(host, port, self, **options) if __name__ == '__main__': app.run()
扩展
class Foo:
#
obj = Foo()
obj() # __call__
obj[x1] = 123 # __setitem__
obj[x2] # __getitem__
obj.x1 = 123 # __setattr__
obj.x2 # __getattr__
SQLhelper
-
方式一
import pymysql import threading from DBUtils.PooledDB import PooledDB """ storage = { 1111:{'stack':[]} } """ class SqlHelper(object): def __init__(self): self.pool = PooledDB( creator=pymysql, # 使用链接数据库的模块 maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数 mincached=2, # 初始化时,链接池中至少创建的链接,0表示不创建 blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错 ping=0, # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always host='127.0.0.1', port=3306, user='root', password='222', database='cmdb', charset='utf8' ) self.local = threading.local() def open(self): conn = self.pool.connection() cursor = conn.cursor() return conn, cursor def close(self, cursor, conn): cursor.close() conn.close() def fetchall(self, sql, *args): """ 获取所有数据 """ conn, cursor = self.open() cursor.execute(sql, args) result = cursor.fetchall() self.close(conn, cursor) return result def fetchone(self, sql, *args): """ 获取所有数据 """ conn, cursor = self.open() cursor.execute(sql, args) result = cursor.fetchone() self.close(conn, cursor) return result def __enter__(self): conn,cursor = self.open() rv = getattr(self.local,'stack',None) if not rv: self.local.stack = [(conn,cursor),] else: rv.append((conn,cursor)) self.local.stack = rv return cursor def __exit__(self, exc_type, exc_val, exc_tb): rv = getattr(self.local,'stack',None) if not rv: # del self.local.stack return conn,cursor = self.local.stack.pop() cursor.close() conn.close() db = SqlHelper()
from sqlhelper import db # db.fetchall(...) # db.fetchone(...) with db as c1: c1.execute('select 1') with db as c2: c1.execute('select 2') print(123)
-
方式二
import pymysql import threading from DBUtils.PooledDB import PooledDB POOL = PooledDB( creator=pymysql, # 使用链接数据库的模块 maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数 mincached=2, # 初始化时,链接池中至少创建的链接,0表示不创建 blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错 ping=0, # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always host='127.0.0.1', port=3306, user='root', password='222', database='cmdb', charset='utf8' ) class SqlHelper(object): def __init__(self): self.conn = None self.cursor = None def open(self): conn = POOL.connection() cursor = conn.cursor() return conn, cursor def close(self): self.cursor.close() self.conn.close() def __enter__(self): self.conn,self.cursor = self.open() return self.cursor def __exit__(self, exc_type, exc_val, exc_tb): self.close()
# ################## 使用 ################## with SqlHelper() as c1: c1.execute('select 1') with SqlHelper() as c2: c2.execute('select 2') print(666) with SqlHelper() as cursor: cursor.execute('select 1') with SqlHelper() as cursor: cursor.execute('select 1')