DAY 95 flask04

1 python的web框架
-django框架,同步框架,3.x以后,加入了异步,websocket
  -1.x对websocket支持比较差
       -2.x channels
       -3.x 可以使用channels,原生支持,asgi协议
  -flask,同步框架
  -orm没有:sqlalchemy
       -session,redis,异步,信号(第三方支持),forms,
   -web.py
   -tornado:异步,老牌,python2上
   -sanic: aiomysql  aioredis,异步orm框架
   -fastapi:
       
   -websocket:应用层协议,为了解决http协议存在的问题,长连接,服务端主动向客户端推送消息
   
2 flask框架
-介绍
   -基本使用
   -登录显示用户信息案例
   -配置文件(使用类方式)
   -路由系统(基本使用和本质)
   -cbv分析
   -模板语法
   -请求响应
   -session(源码:SecureCookieSessionInterface)
   -闪现:flash
   -请求扩展
  -before_request
       -after_request
       -before_first_request
       -teardown_request
       -errorhandler
       -template_global
       -template_filter
   -中间件
  -服务器中间件
       -数据库中间(分库分表,读写分离 mycat)
       -消息队列中间件(redis,rabbitmq(保证数据准确性),kafka(吞吐量)...)
   -蓝图
   -请求上下文分析
   
   
服务和服务之间调用
-http符合restful规范
   -rpc(远程过程调用):rpc框架:grpc...,底层socket,也用http
   -消息队列:
   
   
   
django内置的app中:management
-django自定制命令
   python mangae.py initdb (把在项目路径下的sql文件导入数据库)

 

flask上下文源码回顾

请求上下文执行流程(ctx):
-0 flask项目一启动,有6个全局变量
-_request_ctx_stack:LocalStack对象
-_app_ctx_stack :LocalStack对象
-request LocalProxy对象
-session LocalProxy对象
-1 请求来了 app.__call__()---->内部执行:self.wsgi_app(environ, start_response)
-2 wsgi_app()
-2.1 执行:ctx = self.request_context(environ):返回一个RequestContext对象,并且封装了request(当次请求的request对象),session
-2.2 执行: ctx.push():RequestContext对象的push方法
-2.2.1 push方法中中间位置有:_request_ctx_stack.push(self),self是ctx对象
-2.2.2 去_request_ctx_stack对象的类中找push方法(LocalStack中找push方法)
-2.2.3 push方法源码:
   def push(self, obj):
#通过反射找self._local,在init实例化的时候生成的:self._local = Local()
#Local()flask封装的支持线程和协程的local对象
# 一开始取不到stack,返回None
rv = getattr(self._local, "stack", None)
if rv is None:
#走到这,self._local.stack=[],rv=self._local.stack
self._local.stack = rv = []
# 把ctx放到了列表中
#self._local={'线程id1':{'stack':[ctx,]},'线程id2':{'stack':[ctx,]},'线程id3':{'stack':[ctx,]}}
rv.append(obj)
return rv
-3 如果在视图函数中使用request对象,比如:print(request)
-3.1 会调用request对象的__str__方法,request类是:LocalProxy
-3.2 LocalProxy中的__str__方法:lambda x: str(x._get_current_object())
-3.2.1 内部执行self._get_current_object()
-3.2.2 _get_current_object()方法的源码如下:
   def _get_current_object(self):
if not hasattr(self.__local, "__release_local__"):
#self.__local() 在init的时候,实例化的,在init中:object.__setattr__(self, "_LocalProxy__local", local)
# 用了隐藏属性
#self.__local 实例化该类的时候传入的local(偏函数的内存地址:partial(_lookup_req_object, "request"))
#加括号返回,就会执行偏函数,也就是执行_lookup_req_object,不需要传参数了
#这个地方的返回值就是request对象(当此请求的request,没有乱)
return self.__local()
try:
return getattr(self.__local, self.__name__)
except AttributeError:
raise RuntimeError("no object bound to %s" % self.__name__)
-3.2.3 _lookup_req_object函数源码如下:
def _lookup_req_object(name):
#name是'request'字符串
#top方法是把第二步中放入的ctx取出来,因为都在一个线程内,当前取到的就是当次请求的ctx对象
top = _request_ctx_stack.top
if top is None:
raise RuntimeError(_request_ctx_err_msg)
#通过反射,去ctx中把request对象返回
return getattr(top, name)
-3.2.4 所以:print(request) 实质上是在打印当此请求的request对象的__str__
-4 如果在视图函数中使用request对象,比如:print(request.method):实质上是取到当次请求的reuquest对象的method属性

-5 最终,请求结束执行: ctx.auto_pop(error),把ctx移除掉

其他的东西:
-session:
-请求来了opensession
-ctx.push()---->也就是RequestContext类的push方法的最后的地方:
if self.session is None:
#self是ctx,ctx中有个app就是flask对象,   self.app.session_interface也就是它:SecureCookieSessionInterface()
session_interface = self.app.session_interface
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)
-请求走了savesession
-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放到了那个位置
-app_ctx:
-是:AppContext(self) 对象:封装了当前的app和g
-调用 _app_ctx_stack.push(self) 就是把:app_ctx放到了那个位置

 

1 flask-session


# 使用方式一
from flask_session import RedisSessionInterface,FileSystemSessionInterface,MemcachedSessionInterface
# from redis import Redis
# conn = Redis()
app.session_interface = RedisSessionInterface(redis=conn, key_prefix='flask_xx')


# 使用方式二
from redis import Redis
from flask.ext.session import Session

# 如果在配置类中这么写
# SESSION_TYPE='redis'
# SESSION_REDIS=Redis(host='127.0.0.1',port='6379')

# 如果没有配置类,需要这么写
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = Redis(host='127.0.0.1',port='6379')
app.config['SESSION_KEY_PREFIX'] = 'dasfasdfas'
Session(app)




# 1 关闭浏览器,session失效--》permanent=False
app.session_interface=RedisSessionInterface(conn,key_prefix='lqz',permanent=False)

# 2 session的过期时间是多少?(一般7天)
-timedelta(days=31)
   -flask:31天
   -django:14天
   -drf:token默认7天

2 数据库连接池

https://www.cnblogs.com/liuqingzheng/articles/9006055.html
   
   
#1 pymysql操作数据库
-如果conn,cursor是全局变量,会出现数据混乱
   -如果conn,cursor在视图函数中,会导致多个请求会有多个数据库链接(django就是这样)
 

# 2 在flask中引入数据库连接池
-pip3 install DBUtils

# 3 使用步骤
-第一步,建立一个py文件,创建池对象
   import pymysql
   # 老版本
   # from DBUtils.PooledDB import PooledDB
   # 新版本
   from dbutils.pooled_db import PooledDB
   # 池对象
   POOL = PooledDB(
       creator=pymysql,  # 使用链接数据库的模块
       maxconnections=10,  # 连接池允许的最大连接数,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='test',
       charset='utf8'
  )
   
  -第二步:在视图函数中使用
@app.route('/')
   def index():
       conn = POOL.connection()
       cursor = conn.cursor()
       cursor.execute('select * from employee')
       result = cursor.fetchall()
       print(result)
       return 'hello'

 

3 wtforms(了解,flask版forms)

1 字段校验
form = RegisterForm(formdata=request.form)
if form.validate():
print('用户提交数据通过格式验证,提交的值为:', form.data)
else:
   print(form.errors)
   return render_template('register.html', form=form)
2 模板渲染
<form method="post" novalidate style="padding:0 50px">
  {% for field in form %}
   <p>{{field.label}}: {{field}} {{field.errors[0]}}</p>
  {% endfor %}
   <input type="submit" value="提交">
</form>
3 渲染错误信息

4 信号

#https://www.cnblogs.com/liuqingzheng/articles/9803403.html

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


# 内置信号的使用
## 给内置信号绑定一个函数
def request_finish_01(*args, **kwargs):
   print('请求走了')
   print(args)
   print(kwargs)
signals.request_finished.connect(request_finish_01)


# 自定义信号的使用
## 第0步:定义信号(内置信号,不需要定义)

from flask.signals import _signals
# 定义了一个xxx信号
xxx = _signals.signal('xxx')

## 第一步:定义一个函数,绑定给信号(内置信号的其中一个)
def my_sig(*args, **kwargs):
   print('xxxx')
   print(args)
   print(kwargs)
xxx.connect(my_sig)

## 第二步:触发信号(由于是内置信号,会自动触发,只要触发信号就会执行这个函数)
xxx.send()
原文地址:https://www.cnblogs.com/DEJAVU888/p/14894189.html