flask之cbv分析-模板-请求与响应-session及源码分析-闪现-请求扩展
目录
flask之cbv分析-模板-请求与响应-session及源码分析-闪现-请求扩展
今日内容详细
1 cbv分析
# 基于类的视图 写法
from flask import Flask, request
from flask.views import View, MethodView
# 一般继承MethodView,它已经继承了View
app = Flask(__name__)
app.debug = True
# 视图类 继承MethodView 类中写跟请求方式同名的方法即可 之前学的所有都一致
class IndexView(MethodView):
def get(self):
print(request.method)
return 'get 请求'
def post(self):
print(request.method)
return 'post 请求'
app.add_url_rule('/index', endpoint='index', view_func=IndexView.as_view('index'))
if __name__ == '__main__':
app.run()
1.1 源码分析
# 1 IndexView.as_view('index')执行完的结果 是个函数(view的)内存地址
def as_view(cls, name, *class_args, **class_kwargs):
def view(**kwargs: t.Any) -> ft.ResponseReturnValue:
# 本质是在执行self.dispatch_request 只是用了异步
return current_app.ensure_sync(self.dispatch_request)(**kwargs)
return view
# 2 请求来了 执行view()---> 本质在执行self.dispatch_request---> MethodView中的
def dispatch_request(self, **kwargs):
# self是视图类的对象
meth = getattr(self, request.method.lower(), None)
# 用异步执行meth()
return current_app.ensure_sync(meth)(**kwargs)
# 3 总结:执行原理跟django一样
# 4 路径如果不传别名 别名就是函数名---> 分析一下源码
@app.route('/index')---> 没有传endpoint
endpoint 就是None---> 调用了app.add_url_rule 传入了None
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func) # type:ignore
_endpoint_from_view_func # 就是返回函数的名字
# 5 as_view('index') 必须传参数 传进来的参数是 【别名】
# view是as_view内的内层函数 闭包函数
view.__name__ = name # 修改了函数的名字变成了你传入的
# app.add_url_rule('/index',view_func=IndexView.as_view('index'))
简写成:app.add_url_rule('/index', view_func=view)
# 如果不传参数 所有人的别名(endpoint) 都是内层函数view 所以就报错了
# 6 补充:flask的路由注册使用装饰器 如果写了一个登录认证装饰器 那么应该放在路由装饰器下
放在路由装饰器下
装饰器由外往内执行---> 先执行路由跳转到那个页面 再认证 不通过再下一步
# 7 视图类必须继承MethodView 如果继承View 它的dispatch_request没有具体实现 你的视图类必须重写dispatch_request 我们不想重写 继承MethodView
def dispatch_request(self) -> ft.ResponseReturnValue:
raise NotImplementedError()
# 8 视图类加装饰器 直接配置在类属性上【decorators】即可
decorators = [auth,]
# 源码 cls是视图类 中有decorators
if cls.decorators:
for decorator in cls.decorators:
view = decorator(view) # view=auth(view)
# 源码中学到的
1 as_view 执行流程跟django一样
2 路径如果不传别名 别名就是函数名(endpoint)
3 视图函数加多个装饰器(上下顺序和必须传endpoint---> 不传全是装饰器函数的名字 就报错)
4 视图类必须继承MethodView 否则需要重写dispatch_request
5 视图类加装饰器:类属性decorators = [auth,]
2 模板
2.1 py
from flask import Flask, render_template,Markup
app = Flask(__name__, template_folder='templates', static_folder='static') # 模板的路径必须是templates,因为实例化app对象时,传入的
app.debug=True
def add(a,b):
return a+b
@app.route('/')
def index():
a='<a href="http://www.baidu.com">点我看美女</a>' # 不存在xss攻击,处理了xss
# 渲染链接两种方式 1-在html页面|safe 2-在这里用Markup包一下
a=Markup(a)
return render_template('index.html',name='lqz',a=a,add=add)
if __name__ == '__main__':
app.run()
2.2 html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>模板语法,static</h1>
<img src="/static/1.jpg" alt="">
<h1>模板语法,if</h1>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello World!</h1>
{% endif %}
<h1>模板语法,标签渲染</h1>
{{a|safe}}
{{a}}
<h1>模板语法,执行函数</h1>
{{add(4,5)}}
</body>
</html>
3 请求和响应
# 请求:全局的request对象
# 响应:四件套
from flask import Flask, request, make_response,render_template
app = Flask(__name__)
app.debug = True
@app.route('/', methods=['GET', 'POST'])
def index():
#### 请求
# request.method 提交的方法
# request.args get请求提及的数据
# request.form post请求提交的数据
# request.values post和get提交的数据总和
# request.cookies 客户端所带的cookie
# request.headers 请求头
# request.path 不带域名,请求路径
# request.full_path 不带域名,带参数的请求路径
# request.script_root
# request.url 带域名带参数的请求路径
# request.base_url 带域名请求路径
# request.url_root 域名
# request.host_url 域名
# request.host 127.0.0.1:500
print(request.method)
print(request.args)
print(request.form)
print(request.values)
print(request.cookies)
print(request.headers)
print(request.path)
print(request.full_path)
print(request.url)
print(request.base_url)
print(request.host_url)
print(request.host)
obj = request.files['file']
obj.save(obj.filename)
### 响应 四件套
# 1 响应中写入cookie
# response = 'hello'
# res = make_response(response) # flask.wrappers.Response
# print(type(res))
# res.set_cookie('xx','xx')
# return res
# 2 响应头中写数据(新手四件套,都用make_response包一下)
response = render_template('index.html')
res = make_response(response) # flask.wrappers.Response
print(type(res))
res.headers['yy']='yy'
return res
if __name__ == '__main__':
app.run()
4 session及源码分析
4.1 session的使用
from flask import Flask, request, render_template, session, redirect
app = Flask(__name__)
app.debug = True
app.secret_key = 'fashdaskddsa'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
name = request.form.get('username')
password = request.form.get('password')
print(password)
session['name'] = name
return redirect('/index')
@app.route('/index', methods=['GET', 'POST'])
def index():
return 'hello %s ' % session.get('name', '匿名用户')
if __name__ == '__main__':
app.run()
4.2 源码分析
# cookie:存在于客户端浏览器的键值对
# session:存在于服务端的键值对 # django放在了django_session表中
# flask中 叫session 问题了来了 存哪里了?
加密后 放到了cookie中 如果session发生了变化 我们的cookie也会跟着变
# 源码部分
# 1 app.session_interface 配置了一个类的对象 这个就是session的执行流程
# 2 雷泽有两个非常重要的方法 请求来了 会执行open_session 请求走了会执行save_session
def open_session(self, app, request):
# 1 根据名字 取出前端传入的cookie的value值
val = request.cookies.get(self.get_cookie_name(app))
if not val:
# 2 如果没有value值 构造了一个空的session对象
return self.session_class()
max_age = int(app.permanent_session_lifetime.total_seconds())
try:
# 如果没有过期 解码 做成session对象 后续直接用session即可
data = s.loads(val, max_age=max_age)
return self.session_class(data)
except BadSignature:
# 如果过期了 也是空session
return self.session_class()
def save_session(self, app, session, response):
name = self.get_cookie_name(app)
# 取出过期事件 和把session加密转成字符串 放到cookie中
expires = self.get_expiration_time(app, session)
val = self.get_signing_serializer(app).dumps(dict(session))
response.set_cookie(
name,
val,
expires=expires,
)
'''
扩展:想把session放到redis中 mysql中 已经有人帮咱们写了 第三方
只需要写个类 重写open_session save_session自己写
'''
4.3 session执行原理
5 闪现
# falsh 翻译过来的
当次请求先把一些数据 放在某个位置
下一次请求 把这些数据取出来 取完 就没了
# 作用:
1 可以跨请求 来保存数据
2 当次请求 访问出错 被重定向到其他地址 重定向到这个地址后 拿到当时的错误
# django中有这个东西吗?
message框架
# 用法:
设置 闪现
flask('%s,我错了'%name) # 可以设置多次 放到列表中
flask('超时错误', category='debug') # 分类存
获取 闪现
get_flashed_messages() # 取完就删除
get_flashed_messages(category_filter=['debug']) # 分类取
# 本质 放到session中
6 请求扩展
# 请求扩展中:请求来了 或请求走了 可以绑定一些函数 到这里就会执行这个函数 类似于django的中间件
# 在flask中就用请求扩展 来代替django的中间件
# 好几个请求扩展
before_request:请求来了会走 如果他返回了四件套 就结束了
after_request:请求走了会走 一定要返回response对象
before_first_request:第一次来了会走
teardown_request:无论是否出异常 会走
errorhandler:监听状态码,404 500
template_global:标签
template_filter:过滤器
from flask import Flask, render_template
app = Flask(__name__)
app.debug = True
app.secret_key = 'dhalsidlashdnk'
@app.route('/')
def index():
return '我是首页'
@app.route('/home')
def home():
return render_template('home.html')
# 1 before_request 和 after_request
# 请求来了 执行一个函数 来的时候从上往下执行
@app.before_request
def before():
print('我来了111')
@app.before_request
def before2():
print('我来了222')
# 请求走了 执行一个函数 走的时候 从下往上执行
@app.after_request
def after(response):
print('我不打扰 我走了哈111')
return response
@app.after_request
def after2(response):
print('我不打扰 我走了哈222')
return response
if __name__ == '__main__':
app.run()
'''
注意走的时候必须传入response并return出去
'''
# 2 项目启动后的第一个请求触发
@app.before_first_request
def first():
print('我只在第一次请求时触发')
# 3 teardown_request 无论视图函数是否出错 都会执行它 做错误日志
@app.teardown_request
def teardown(e):
print(e)
print('执行我了')
# 4 errorhandler 监听响应状态码 如果符合监听的状态码 就会走它
@app.errorhandler(404)
def error_404(arg):
return '404错误了'
@app.errorhandler(500)
def error_500(arg):
return '500错误了'
# 5 template_global 在模板中直接使用该过滤器
@app.template_global()
def add(a1, a2):
return a1+a2
# 6 template_filter
@app.template_filter()
def db(a1, a2, a3):
return a1+a2+a3
'''
<h1>标签和过滤器</h1>
{{add(8,8)}}---> 16
{{88|db(2,3)}}---> 93
'''
每日补充
1 脏读 不可重复读 幻读 mysql5.7以后默认隔离级别是什么?
# 脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据
# 当一个事务正在多次修改某个数据 而在这个事务中这多次的修改还未提交 这时一个并发的事务来访问该数据 就会导致两个事务得到的数据不一致
# 不可重复读是指在对于数据库中的某个数据 一个事务范围内多次查询却返回了不同的数据值 这是由于在查询间隔 被另一个事务修改并提交了
# 不可重复读和脏读的区别是 脏读是某一事务读取了另一个事务未提交的脏数据 而不可重复读则是读取了前一事务提交的数据
# 幻读是事务非独立执行时发生的一种现象 例如事务1对一个表中的数据进行修改1到2的操作;此时事务2对这个表中插入了一行数据项 而事务2的数值还是1并且提交给数据库;事务1的用户查看刚刚修改的数据 发现自己修改未生效 其实只是事务2将该数据又改了回来 就像幻觉一样 这就是幻读
# mysql5.7以后默认隔离级别是可重复读
2 什么是qps tps 并发量 pv uv
# Transactions Per Second 意思是每秒事务数。一个事务是指客户端向服务器发送请求然后服务器做出反应的过程 具体的事务定义 可以是一个接口 多个接口 一个业务流程等等
# Queries Per Second 意思是每秒查询率。指一台服务器每秒能够响应的查询次数 用于衡量特定的查询服务器在规定时间内所处理流量多少 主要针对专门用于查询的服务器的性能指标
# 并发量指的是系统同时处理的请求或操作数量
# 页面浏览量 通常是衡量一个网络新闻频道或者网站甚至一条网络新闻的主要指标。网页浏览数是评价网站流量最常用的指标之一 简称PV
# UV是unique visitor的简写 一般指独立访客
# 一般的 我们可以用两个数值标准来统计访问某网站的访客 即访问次数和独立访客数
3 什么是接口幂等性问题 如何解决?
# 接口幂等性就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的 不会因为多次点击而产生了副作用
# 举个支付的例子 用户购买商品后支付 支付扣款成功 但是返回结果的时候网络异常 此时钱已经扣了 用户再次点击按钮 此时会进行第二次扣款 返回结果成功 用户查询余额发现多扣钱了 流水记录也变成了两条 这就没有保证接口的幂等性
# 如何保证幂等性---> 最好只答token方法解决方法
# 1 乐观锁
# 如果只是更新已有的数据 没有必要对业务进行加锁 设计表结构时使用乐观锁 一般通过version来做乐观锁 这样既能保证执行效率 又能保证幂等
# 2 防重锁
# 使用订单号orderNo作为去重表的唯一索引 每次请求都根据订单号向去重表中插入一条数据。第一次请求查询订单支付状态 订单没有支付 进行支付操作 无论成功与否 执行完后更新订单状态为成功或失败 删除去重表中的数据。后续的订单因为表中唯一索引而插入失败 则返回操作失败 直到第一次的请求完成(成功或失败)。可以看出防重表作用是加锁的功能
# 3 分布式锁
# 这里使用防重表可以使用分布式锁代替 比如redis。订单发起支付请求 支付系统会取redis缓存中查询是否存在该订单号的Key 如果不存在 则向redis增加Key为订单号。查询订单支付与否 如果没有支付则进行支付 支付完成后删除该订单号的Key。通过redis做到了分布式锁 只有这次订单支付请求完成 下次请求才能进来。相比去重表 将并发做到了缓存中 较为高效。思路相同 同一时间只能完成一次支付请求
# 4 token令牌
# 这种方式分成两个阶段:申请token阶段和支付阶段
# 第一阶段 在进入到提交订单页面之前 需要订单系统根据用户信息向支付系统发起一次申请token的请求 支付系统将token保存到redis缓存中 为第二阶段支付使用
# 第二阶段 订单系统拿着申请到的token发起支付请求 支付系统会检查redis中是否存在该token 如果存在 表示第一次发起支付请求 删除缓存中的token后开始支付逻辑处理;如果缓存中不存在 表示非法请求
# 5 支付缓冲区
# 把订单的支付请求都快速的接下来 一个快速接单的缓冲管道。后续使用异步任务处理管道中的数据 过滤重复的待支付订单。优点是同步转异步 高吞吐。不足是不能及时返回支付结果 需要后续监听支付结果的异步返回