文章目录
Flask常用组件
一、 flask_session
session
是基于cookie实现, 保存在服务端的键值对(形式为 {随机字符串:'uuid'}
), 同时在浏览器中的cookie中也对应一相同的随机字符串,用来再次请求的 时候验证,这个组件的作用是将session数据存储到数据库中
1、 常用配置
# 对 session 进行保护
SECRET_KEY = os.urandom(32) # 创建密钥
SESSION_USE_SIGNER = True # 是否签名保护
PERMANENT_SESSION_LIFETIME = timedelta(minutes=30) # 设置时间延迟
SESSION_REFRESH_EACH_REQUEST = True # 每刷新一次就更新一次session
SESSION_TYPE = "redis" # 设置redis存储session数据
SESSION_REDIS = Redis("127.0.0.1") # 连接数据库
SESSION_PREFIX = "MyWebPrefix:" # 设置session前缀,默认为session:
注意:
session
中存储的是字典,当修改字典内部(第二级)元素时,会造成数据不更新解决方法:
我们要先阅读一下部分源码
class SecureCookieSession(CallbackDict, SessionMixin): #: When data is changed, this is set to ``True``. Only the session modified = False # 只有当session里面的数据改变的时候,这个才会被设置成True #: When data is read or written, this is set to ``True``. Used by accessed = False def __init__(self, initial: t.Any = None) -> None: def on_update(self) -> None: self.modified = True # 如果数据更新,就会被改为True,后面就会对session设置到cookies中 """ if not (session.modified # 如果session修改,就会重新设置到cookies中 or ( session.permanent # 是否设置到cookie里面 and app.config["SESSION_REFRESH_EACH_REQUEST"] # 如果为True,则每次请求刷新一次 )): return ... response.set_cookie(...) """ self.accessed = True # 如果session已读,则将session写入response中 """ if session.accessed: response.vary.add("Cookie") """ # 将on_update()传递给CallbackDict super().__init__(initial, on_update)
解决方法:
方法一
session.modified = True # 手动使得session标记为session的内容被修改
方法二
app.config["SESSION_REFRESH_EACH_REQUEST"] = True # 每刷新一次就更新 # 同时,在登录成功之后,设置 session.permanent = True # 默认为 False,但是如果使用redis的话,就不需要设置,其默认为True
2、 使用方法
2.1 session_interface
通过session_interface
来设置
from flask_session import RedisSessionInterface
from flask import Flask
from os import urandom
from redis import Redis
app = Flask(__name__)
app.serect_key = urandom(32) # 设置32位随机密钥
app.config["SESSION_USE_SIGNER"] = True
# 通过redis保存session
app.session_interface = RedisSessionInterface(
redis=Redis("127.0.0.1"), # 连接Redis数据库
key_prefix="flask_login", # 设置随机字符串的前缀,默认为session:
)
默认session设置为
from flask.sessions import SecureCookieSessionInterface app.session_interface = SecureCookieSessionInterface()
修改后,就可以直接使用了,其使用方法和原先的一样
2.2 config
通过配置文件来设置
from flask_session import Session
from flask import Flask
from os import urandom
from redis import Redis
settings = {
"SESSION_TYPE": "redis", # 使用redis数据库连接
"SESSION_REDIS": Redis("127.0.0.1"), # 连接redis数据库
"SECRET_KEY": os.urandom(32), # 创建密钥
"SESSION_USE_SIGNER": True, # 是否签名保护
"SESSION_REFRESH_EACH_REQUEST": True, # 每刷新一次就更新数据
}
app.config.from_mapping(settings) # 设置配置
Session(app) # 内部封装了设置app.session_interface的方法
二、 DBUtils
1、 引言
当我们要对数据库进行操作时,可以这么干:
import pymysql
from functools import wraps
SQL_CONFIG = {
# 对数据库的配置
"host": "127.0.0.1",
"port": 3306,
"user": "root",
"passwd": "qwe123",
"db": "flask1",
}
def sql_outer(fun):
"""使用装饰器,可以对数据库便捷操作"""
@wraps(fun)
def inner(sql, *args):
conn = pymysql.connect(**SQL_CONFIG) # 连接数据库
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 返回字典类型的SQL数据
obj = fun(sql, cursor, *args) # 将参数传入函数中,进行运行
conn.commit() # 提交事务
cursor.close() # 关闭游标
conn.close() # 关闭连接
return obj # 返回数据
return inner
class SQLTool:
@staticmethod
@sql_outer
def fetch_all(sql: "sql 语句", cursor=None, *args: "要查询的数据"):
# print(*args)
cursor.execute(sql, *args) # 注意不能使用字符串的拼接,防止SQL注入
obj = cursor.fetchall() # 获取全部数据
return obj
@staticmethod
@sql_outer
def fetch_one(sql: "sql 语句", cursor=None, *args: "要查询的数据"):
cursor.execute(sql, *args) # 注意不能使用字符串的拼接,防止SQL注入,同时要对元组进行解包
obj = cursor.fetchone() # 获取第一条数据
return obj
if __name__ == '__main__':
obj = SQLTool.fetch_one("SELECT id, name FROM users WHERE name=%s and pwd=%s", ["kun", "123"])
print(obj)
问题来了,如果有很多连接的话,开启数据库再关闭是不是很麻烦呢?
- 我们可以使用数据库连接池:
DBUtils
2、 DBUtils
使用数据库连接池
此连接池有两种模式:
- 为每一个线程创建一个连接,线程即使调用了close方法,也不会关闭,只是把连接重新发到连接池,供自己线程再次使用。当线程终止,连接自动关闭
- 创建一批连接到连接池,供所有线程共享使用(主要)
2.1 模式一
POOL = PersistentDB(
creator=pymysql, # 使用链接数据库的模块
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
closeable=False,
# 如果为False时, conn.close() 实际上被忽略,供下次使用,再线程关闭时,才会自动关闭链接。如果为True时, conn.close()则关闭链接,那么再次调用pool.connection时就会报错,因为已经真的关闭了连接(pool.steady_connection()可以获取一个新的链接)
threadlocal=None, # 本线程独享值得对象,用于保存链接对象,如果链接对象被重置
host='127.0.0.1',
port=3306,
user='root',
password='123',
database='pooldb',
charset='utf8'
)
def func():
conn = POOL.connection(shareable=False)
cursor = conn.cursor()
cursor.execute('select * from tb1')
result = cursor.fetchall()
cursor.close()
conn.close()
func()
2.2 模式二
import time
import pymysql
import threading
from dbutils.pooled_db import PooledDB, SharedDBConnection
POOL = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,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='123',
database='pooldb',