闪现(flash)
# 作用
访问a页面,出了错,重定向到了b页面,要在b页面线上a页面的错误信息
在某个请求中放入值,另一个请求中取出,取出来后就没了
# 使用方式一:
设置值:
flash('不好意思,没有权限看')
可以用多次
取值:取出列表
get_flashed_messages()
# 使用方式二:分类设置和获取
设置值:
flash('钱钱钱',category='lqz')
flash('666',category='c1')')
可以用多次
取值:取出列表
errors = get_flashed_messages(category_filter=['lqz'])
from flask import Flask, render_template, redirect, flash, get_flashed_messages
app = Flask(__name__)
app.secret_key = 'jhBdbnkasxkx'
@app.route('/', methods=['GET', 'POST'])
def index():
errors = get_flashed_messages(category_filter=['zxr'])
return render_template('index.html', errors=errors)
@app.route('/goods', methods=['GET', 'POST'])
def goods():
flash('wakaka', category='zxr')
flash('yiyawei', category='zxb')
return redirect('/')
if __name__ == '__main__':
app.run(debug=True, port=8082)
异步框架
# 异步框架 FastAPI
async def index():
print('jnaax')
a++
await XXX
async def goods():
pass
# 框架之前的web框架,开启进程,线程,一条线程会运行多个协程函数,协程函数中遇到IO函数,读到await关键字,就会切换到别的协程函数
# 一旦使用异步,以后所有的模块,都要异步
-pymysql :同步的
-redis :同步
-aiomysql:异步
-aioredis:异步
-在fastapi或sanic中,要操作mysql,redis要使用异步的框架,否则效率更低
-django 3.x 以后页支持async 关键字
-没有一个特别好异步的orm框架
-sqlalchemy在做
-tortoise-orm
https://tortoise-orm.readthedocs.io/en/latest/index.html
# aiomysql
import asyncio
import aiomysql
loop = asyncio.get_event_loop()
async def test_example():
conn = await aiomysql.connect(host='127.0.0.1', port=3306,
user='root', password='', db='mysql',
loop=loop)
cur = await conn.cursor()
await cur.execute("SELECT Host,User FROM user")
print(cur.description)
r = await cur.fetchall()
print(r)
await cur.close()
conn.close()
loop.run_until_complete(test_example())
# aioredis
import aioredis
import asyncio
class Redis:
_redis = None
async def get_redis_pool(self, *args, **kwargs):
if not self._redis:
self._redis = await aioredis.create_redis_pool(*args, **kwargs)
return self._redis
async def close(self):
if self._redis:
self._redis.close()
await self._redis.wait_closed()
async def get_value(key):
redis = Redis()
r = await redis.get_redis_pool(('127.0.0.1', 6379), db=7, encoding='utf-8')
value = await r.get(key)
print(f'{key!r}: {value!r}')
await redis.close()
if __name__ == '__main__':
asyncio.run(get_value('key')) # need python3.7
请求扩展
# 在请求进入视图函数之前,执行一些代码
# 请求出了视图函数以后,执行一些代码
# 类似于django的中间件完成的功能
# 1 before_request:在请求进视图函数之前执行
多个的话,会从上往下,依次执行, django:process_request
如果返回四件套之一,就直接返回了,就不会再往下走了
在这里面,正常使用request对象,就是当次请求的request
#2 after_request:在请求从视图函数走之后执行
多个的话,会从下往上,依次执行, django:process_response一样
要有参数,和返回值,参数就是response对象,返回值也必须是resposne对象
session,request 照常使用
向响应头写东西?向cookie中写东西
解决跨域问题再来这里解决就好了
# 3 before_first_request:项目启动后,第一次访问会执行,以后再也不执行了
可以做一些初始化的操作
现在已经弃用掉了
# 4 teardown_request:每一个请求之后绑定一个函数,即使遇到了异常,每个请求走,都会执行,记录错误日志
@app.teardown_request
def tear_down(e):
print(e) # 如果有异常,这是异常对象
print('我执行了')
# 5 errorhandler路径不存在时404,服务器内部错误500
# @app.errorhandler(404)
# def error_404(arg):
# print('404会执行我')
# # return "404错误了"
# return render_template('404.html')
@app.errorhandler(500) # debug为False请情况下才能看到
def error_500(arg):
print('500会执行我')
return "服务器内部错误"
# 6 template_global 标签 ,在模板中用 {{sb(1,2)}}
@app.template_global()
def sb(a1, a2):
return a1 + a2
前端:{{sb(3, 4)}}
# 7 template_filter过滤器 在模板中用 {{10|db(1,2)}}
@app.template_filter()
def db(a1, a2, a3):
return a1 + a2 + a3
过滤器的使用:{{ 1|db(2,3) }}
蓝图
# blueprint:对目录进行划分,因为之前所有代码都写在一个py文件中,后期肯定要分到多个文件中
# 蓝图就是为了划分目录的
# 使用步骤:
-1 在不同的view的py文件中,定义蓝图
-2 使用app对象,注册蓝图
-3 使用蓝图,注册路由,注册请求扩展
# 不用蓝图划分目录
# 目录结构
flask_blueprint
-static # 静态文件存放位置
-templates # 模板存放位置
-user.html # 用户html页面
-views # 视图函数的py文件
-__init__.py # 里面定义了Flask的app对象
goods.py # 商品相关视图
user.py # 用户相关视图
app.py #启动文件
# 蓝图小型项目
flask_blueprint_little # 项目名
-src # 项目代码所在路径
-__init__.py # app对象创建的地方
-templates # 模板
-user.html
-static # 静态文件
-views # 视图函数存放位置
-user.py # 用户相关视图
-order.py # 订单相关视图
-manage.py # 启动文件
# 大型项目
flask_blurprint_big # 项目名字
-src # 项目代码所在位置
-__init__.py # src的init,falsk,app实例化
-settings.py # 配置文件
-admin # 类似于django的admin app
-__init__.py # 蓝图初始化
-template # 模板
-backend.html
-static # 静态文件
-xx.jpg
-views.py # 视图层
-models.py # models层,后期咱们表模型
-api
-__init__.py
-template
-static
-models.py
-views.py
-manage.py # 启动文件
g对象
# 专门用来存储用户信息的g对象,g的全称为global,是一个全局的,g对象在整个Flask应用运行期间都是可以使用的,并且也跟request一样是线程隔离的,一般使用就是,将一些经常会用到的数据绑定到上面,以后就直接从g上面取就可以了,而不需要通过传参的形式,这样更加方便。
# 作用
其实是请求的上下文,从请求进来,就有,到请求走了,一直存在,所以在当次请求过程中,如果调用别的函数,不需要把参数传入,只需要放到g对象中,在别的函数中直接使用g获取即可
# g对象在一次请求中的所有代码的地方,都是可以使用的
# g对象和session的区别
session对象可以是跨request的,只要session还为失效,不同的request的请求会获取到同一个session,但是g对象不是,g对象不需要管过期时间,请求一次就g对象就改变了一次,或者重新赋值了一次
# g对象的导入
from flask import g
# django中有咩有这个东西?
request.context
# 上下文的机制
如果直接放入request对象中,可能会把原来的属性替换掉
from flask import Flask, request, g
app = Flask(__name__)
from flask import g
def funa():
print('funa %s' % g.uname)
def funb():
print('funb %s' % g.uname)
def func():
print('func %s' % g.uname)
@app.route("/profile/")
def my_profile():
# 从url中取参
uname = request.args.get('uname')
# 调用功能函数办理业务
# 引入g对象
g.uname = uname
funa()
funb()
func()
return "办理业务成功"
if __name__ == '__main__':
app.run(debug=True)
# http://127.0.0.1:5000/profile/?uname=zxr
flask-session
# flask内置的session,把数据加密后保存到浏览器了
# 能不能自己写个session的类,重写open_session和save_session,把数据保存到服务端的redis中
# 安装 pip install -U flask-session # 升级安装
# RedisSessionInterface 继承SessionInterface,里面重写了open_session和save_session
save_session,去redis中删除、设置
open_session,取出随机字符串,根据随机字符串去redis中
val = self.redisget(self.key_prefix + sid)
val在loads后再转到随机字符串中
跟我们之前的逻辑是一样的,只是它现在去redis中取存
# 使用:
方式一:以后保存到redis中
from flask_session.sessions import RedisSessionInterface
from redis import Redis
conn=Redis(host='127.0.0.0',port=6379)
# app.session_interface = RedisSessionInterface(redis=None, key_prefix='lqz')
app.session_interface = RedisSessionInterface(redis=conn, key_prefix='zxr')
# 在视图函数中使用即可
@app.route('/')
def index():
session['name'] = 'pyy'
return 'hello'
方式二: 通用方案,以后集成第三方插件,大致都按这个流程
from flask_session import Session
# 配置文件
app.config.from_pyfile('settings.py')
# from redis import Redis
# app.config['SESSION_TYPE'] = 'redis'
# app.config['SESSION_REDIS'] = Redis(host='127.0.0.1',port='6379')
Session(app) # 导入一个类,把app传入
# 1 如何配置session的过期时间
配置文件:PERMANENT_SESSION_LIFETIME = timedelta(seconds=10)
# 2 如何让cookie,关闭浏览器就失效
# expires 设置为None,就是浏览器关闭cookie就失效了
res = make_response('hello')
res.set_cookie('name', 'lqz', expires=None)
#session设置的cookie,关闭浏览器失效
-使用方式一:app.session_interface = RedisSessionInterface(redis=conn, key_prefix='lqz',permanent=False)
-使用方式二:配置文件加入
SESSION_PERMANENT=False
数据库连接池
pymsql连接数据库
import pymysql
# 连接数据库,host、port、user、password、db
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='s8day127db')
# 游标
cursor = conn.cursor()
cursor.execute('select * from boy')
res = cursor.fetchall()
cursor.close()
conn.close()
# 上述代码存在的问题就是,每次请求过来以后都要打开mysql连接,操作,操作完以后关闭连接
# 如果并发量很高,如果有1w个并发,要开1w mysql的连接,mysql顶不住
# 解决方案:把连接对象和cursor定义成全局的
每个视图函数使用同一个curor,这样会错乱
数据库连接池版
# 使用第三方的:DBUTILs,创建数据库连接池
# pip install -U DBUTILs
# 使用步骤
1 在py中实例化一个池
from dbutils.pooled_db import PooledDB
import pymysql
2 实例化得到对象
POOL = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
maxcached=5, # 链接池中最多闲置的链接,0和None不限制
maxshared=0, # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制
setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
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='123',
database='cnblogs',
charset='utf8'
)
3 在视图函数中使用池
from db import POOL
@app.route('/boys')
def boys():
# 第一步,从连接池中取一个链接
conn = POOL.connection()
cursor = conn.cursor()
time.sleep(0.01)
cursor.execute('select * from article')
res = cursor.fetchall()
print(res)
return jsonify(res)
# 总结:
如果使用池:无论客户端连接数有多大,mysql的连接数,最多就是6个
如果不使用池:mysql的连接数会过大,把mysql崩掉
# 压力测试:客户端代码
import requests
from threading import Thread, get_ident
def task():
res = requests.get('http://127.0.0.1:8080/boys')
print('线程id号为:%s,获取的数据为:' % str(get_ident()), res.json())
if __name__ == '__main__':
for i in range(5000):
t = Thread(target=task)
t.start()
#链接mysql 查看连接数
# mysql查询语句
show status like 'Threads%'