【flask】蓝图的使用方式 g对象的使用 flask配置数据库连接池

上节回顾

全局request对象。线程会处理请求,确保线程中的数据不错乱。
django_session表:

image-20230404102119106

# 1 cbv 使用
	-写一个类继承MethodView,写get,post。。。
    -类属性decorators = [auth,]可以加装饰器
    
# 2 cbv执行流程
	-1 跟djagno流程一样
    -2 entpoint 作用路径别名,add_url_rule(view_func=IndexView.as_view('index'))
    -3 为什么entpoint不传,是被路由装饰器装饰的函数名:   函数.__name__
    -4 装饰器的执行先后顺序 
    
    
# 3 模板语法

# 4 请求响应
	-请求:全局request对象,在不同视图函数中尽管使用,不会错乱
    	-method
        -path
        -files
        -form
        -argvs。。。。
    -响应:四件套
    	-响应对象,make_response包裹一下四件套之一
        -set_cookies
        -响应对象.headers 响应头
        
# 5 session使用
	-设置秘钥
    -全局导入,直接赋值,取值
    
# 6 session执行流程
	-open_session:前端写到cookie到后端,后端取出cookie对应的value值,解密,转到session对象中,后续再视图函数中,使用session即可
    -save_session:请求走的时候,校验session有没有被改过,如果被改了,删除cookie,重新设置cookie
    
    
    
    -session用起来像字典---》如何做,一个对象可以像字典一样使用, __getitem__  __setitem__,只要调用__setitem__ 就说明动了,对象属性 modify,一开始false,只要触发了__setitem__,置为true,后期只要判断modify,就可以判断session有没有被改过
    
    
    
# 7 闪现:跨请求获取到之前请求存放的数据,取一次就没了 关注django的message框架
	-flash('%s,我错了'%name)
    -get_flashed_messages()
    
    
# 8 请求扩展
	-before_request
    -after_request
    -before_first_request
    -teardown_request:错误日志记录
    -errorhandler:是某种状态码,就会执行它

今日内容

1 蓝图的使用

# blueprint 翻译过来的,称之为蓝图 
# 作用是:之前全在一个py中写flask项目,后期肯定要划分目录

# 不用蓝图,划分目录
	no_blueprint_flask  # 项目名
        src             #核心源码位置
            __init__.py # 包 里面实例化得到了app对象,
            models.py   #放表模型
            views.py    # 放视图函数
        static          # 放静态资源
        templates       # 放模板
        	home.html   # 模板
        manage.py       # 启动文件

        
        
# 蓝图的使用步骤
	-第一步:导入蓝图类           			    from flask import Blueprint
    -第二步:实例化得到蓝图对象    				  us=Blueprint('user',__name__)
    -第三步:在app中注册蓝图      				app.register_blueprint(us)
    -第四步:在不同的views.py 使用蓝图注册路由      @us.route('/login')
    -补充:蓝图可以有自己的静态文件和模板
    -补充:注册蓝图时,可以使用前缀,必须以/ 开头
# 使用蓝图,划分小型项目目录
    little_blueprint              # 项目名
        -src                      # 核心代码
            -static               # 静态文件
                -1.jpg            # 图片
            -templates            # 模板文件
                -user.html        # 模板
            -views                # 视图函数存放位置
                -order.py         # 订单相关视图
                -user.py          # 用户相关视图
            -__init__.py          # 包
            -models.py            # 表模型
        -manage.py                # 启动文件
	

# 使用蓝图,划分大型项目目录  多个app,像django一样
big_blueprint  								# 项目名
    -src									# 核心文件
        -admin								# admin的app
        	-static							# 静态文件
        		-1.jpg						# 图片
        	-templates						# 模板文件目录
        		-admin_home.html			# 模板文件
        	-__init__.py					# 包
        	-models.py						# 表模型
        	-views.py						# 视图函数
        -home								# home app
        -order								# orderapp
        -__init__.py						# 包
        -settings.py						# 配置文件
    -manage.py								# 启动文件

不使用蓝图flask项目划分目录结构:
image-20230404102743912

在init文件下:

image-20230404102857009manage.py:
image-20230404102833608

添加view.py:
image-20230404103049643

修改templates:
image-20230404103124092

启动项目会报错,路由没找到,因为view代码根本就没执行:
image-20230404103400840

需要在init执行。

使用蓝图,划分小型项目目录:
image-20230404104237014

在views中使用蓝图:

image-20230404104442404

导入蓝图类,产生了蓝图对象,使用蓝图对象注册了路由。

image-20230404164743535

在app中注册蓝图:

image-20230404104902498

在manage.py中导入app启动:

image-20230404104825684

启动项目 --> 执行manage.py --> 导入src包 --> 执行src包的init --> 产生flask app --> 添加配置文件 --> 导入视图中的蓝图 --> 蓝图注册路由 --> 注册蓝图 --> 回到manage.py执行app.run()

蓝图的请求扩展,只专注于被蓝图管理的视图:

image-20230404152542934

使用蓝图,划分大型项目目录:(有多个app)
image-20230404105956848

蓝图的初始化可以写在views.py或者app下的init:

image-20230404110848657

我们选择在 app init下创建蓝图对象,然后在view中导入蓝图,使用蓝图注册路由。

使用蓝图注册路由:

image-20230404110715278

也可以模拟django的urls.py:
image-20230404110231834

在src的init注册各个app下的蓝图:

image-20230404110735104

url_prefix相当于做路由分发。

2 g对象

# g 对象 是什么?
	-global的缩写,再python中是个关键字,不能以关键字作为变量名,干脆用了g
    -g 对象,在整个请求的全局,可以放值,可以取值
    -全局变量,在任意位置导入使用即可
    
    -它为什么不学django使用request作为上下文?
    	-因为使用request,可能会造成request数据的污染,不小心改了request的属性,但你不知道
        -建议使用g 是空的,放入之后在当次请求中全局优先
    
    
    
# 以后想在当次请求中,放入一些数据,后面使用,就可以使用g对象 
    
    
    
    
# g和session有什么区别?
	-g 是只针对于当次请求
    -session针对于多次请求
    

image-20230404114458006

为什么不学django使用request作为上下文:
因为可能会造成数据的污染,不小心修改了request的属性但是你不知道。

使用案例:

from flask import Flask, g, request

app = Flask(__name__)
app.debug = True


@app.before_request
def before():
    if 'home' in request.path:
        g.xx = 'xx'


def add(a, b):
    # print('---',g.name)
    print('---', request.name)
    return a + b


@app.route('/')
def index():
    print(g.xx)
    name = request.args.get('name')
    # g.name = name
    request.method = name
    res = add(1, 2)
    print(res)
    return 'index'


@app.route('/home')
def home():
    print(g.xx)
    return 'index'


if __name__ == '__main__':
    app.run()

g对象 vs request对象

g对象和request对象都是Flask提供的全局对象,它们都可以用来在一个请求的上下文中存储和获取数据。但是,它们之间也有一些区别和优劣:

  • g对象是一个自定义的对象,你可以在它上面存储任何你想要的数据,而request对象是一个封装了HTTP请求内容的对象,它有一些固定的属性和方法,比如request.method, request.args, request.form等,你不能在它上面随意添加或修改数据。
  • g对象只在请求处理过程中有效,一旦请求结束,g对象就会被销毁,而request对象会被发送到客户端,并在下次请求时被重新构建。这意味着g对象不能用来存储跨请求的数据,而request对象可以(通过session属性)。
  • g对象是线程隔离的,也就是说每个请求都有自己的g对象,不会和其他请求的g对象混淆,而request对象是线程共享的,也就是说多个请求可能会同时访问同一个request对象。这意味着g对象可以保证数据的安全性和一致性,而request对象可能会出现数据竞争或覆盖的问题。

总的来说,g对象和request对象各有各的用途和优势,你应该根据不同的场景选择合适的对象来使用。一般来说:

  • 如果你想要存储一些自定义的数据,并且只在当前请求中使用,那么你可以使用g对象。
  • 如果你想要获取或修改一些HTTP请求相关的数据,并且可能需要跨请求保存,那么你可以使用request对象。

举例来说:

  • 如果你想要在请求开始之前获取当前用户的信息,并且在后续的视图函数或模板中使用,那么你可以使用g对象来存储用户信息。例如:
from flask import g

@app.before_request
def load_user():
    g.user = get_current_user()

@app.route("/profile")
def profile():
    return render_template("profile.html", user=g.user)
  • 如果你想要在视图函数中获取或修改一些表单数据,并且需要在下次请求时记住这些数据,那么你可以使用request对象来获取或修改表单数据,并且使用session属性来保存数据。例如:
from flask import request, session

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form.get("username")
        password = request.form.get("password")
        if validate(username, password):
            session["username"] = username
            return redirect(url_for("index"))
        else:
            return "Invalid credentials"
    else:
        return render_template("login.html")

3 数据库连接池

# flask 操作mysql
	-使用pymysql
    -在视图函数中,创建pymysql的连接,查数据,查完,返回给前端
    	-有什么问题? 来一个请求,创建一个连接,请求结束,连接关闭  (djanog就是这么做的)
        
    -把连接对象,做成全局的,在视图函数中,使用全局的连接,查询,返回给前端
    	-有什么问题?会出现数据错乱,详见下图
        
# 解决上面的两个问题
	-数据库连接池
    -创建一个全局的池
    -每次进入视图函数,从池中取一个连接使用,使用完放回到池中,只要控制池的大小,就能控制mysql连接数
    
    
    
# 使用第三方数据库连接池,使用步骤
	-1 安装 pip install dbutils
    -2 使用:实例化得到一个池对象
       
from dbutils.pooled_db import PooledDB
import pymysql
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=[],  # 开始会话前执行的命令列表。如:["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='',
    database='cnblogs',
    charset='utf8'
)
       
     
	-3 在视图函数中导入使用
        conn = pool.connection()
        cursor = conn.cursor(pymysql.cursors.DictCursor)
        cursor.execute('select id,title,author_img from aritcle limit 2')
        res = cursor.fetchall()
        
        
        
        
        
 #  带池的代码
@app.route('/article_pool')
def article_pool():
    conn = pool.connection()
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    cursor.execute('select id,title,author_img from aritcle limit 2')
    res = cursor.fetchall()
    print(res)
    return jsonify(res)

# 不带池的代码
@app.route('/article')
def article():
    conn = pymysql.connect(user='root',
                           password="",
                           host='127.0.0.1',
                           database='cnblogs',
                           port=3306)
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    time.sleep(random.randint(1,3))
    cursor.execute('select id,title,author_img from aritcle limit 2')
    res = cursor.fetchall()
    cursor.close()
    conn.close()
    return jsonify(res)



# 压力测试代码
from threading import Thread
import requests


def task():
    res = requests.get('http://127.0.0.1:5000/article_pool')
    print(len(res.text))


if __name__ == '__main__':
    for i in range(500):
        t = Thread(target=task)
        t.start()
## 效果是:
	使用池的连接数明显小
    不使用池连接数明显很大
    
    
# 查看数据库连接数
show status like 'Threads%'

在flask中使用pymysql:

image-20230404115638691

令execute返回的是列表套字典:

image-20230404115932456

使用cursor=pymysql.cursors.DictCursor

每个请求,都建立一个数据库连接。但是请求数多的情况,会导致mysql连接数过大。

将连接对象做成全局变量:全局cursor做单例模式。

image-20230404120452669

这种方式会出现数据错乱,通常不会使用。

使用连接池控制mysql连接数:

image-20230404121143944

使用连接池:

image-20230404121522868

写个脚本测试使用连接池和不使用连接池的区别(压力测试):

查看数据库状态,不带连接池:

image-20230404122145654

带连接池:

image-20230404122238436

mysql的最大连接数有几千,集群化部署会顶不住。

posted @ 2023-04-04 20:22  passion2021  阅读(73)  评论(0编辑  收藏  举报