闪现(flash)、异步框架、请求扩展、蓝图、g对象、flask-session、数据库连接池

闪现(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%'
posted @ 2022-12-13 19:22  张张包~  阅读(252)  评论(0编辑  收藏  举报