一、 flask-session
1 用处:用来替换flask内置的session,支持存到redis,存到数据库
2 flask-session如何使用
方式一:
conn=redis.Redis(host='127.0.0.1',port=6379)
app.session_interface=RedisSessionInterface(conn,'wmt',permanent=False)
from flask import session,Flask
from flask_session import RedisSessionInterface,MongoDBSessionInterface #除了这个数据库还有其他数据库类型
import redis
app = Flask(__name__)
#第一种用法
conn = redis.Redis(host='127.0.0.1',port=6379) #链接Redis数据库
app.session_interface=RedisSessionInterface(conn,'wmt')
@app.route('/')
def index():
return 'hello'
if __name__ == "__main__":
app.run()
方式二:
from redis import Redis
from flask.ext.session import Session
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_KEY_PREFIX'] = 'wmt'
app.config['SESSION_REDIS'] = Redis(host='127.0.0.1',port='6379')
# # 本质跟上面一样
# Session(app)
from flask import Flask
from redis import Redis
from flask.ext.session import Session
app = Flask(__name__)
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_KEY_PREFIX'] = 'wmt'
app.config['SESSION_REDIS'] = Redis(host='127.0.0.1',port='6379')
#本质和第一种用法一样
Session(app)
@app.route('/')
def index():
session[]
return 'hello'
if __name__ == "__main__":
app.run()
注意:
-关闭浏览器时让cookie失效:permanent=False,设置这个参数
app.session_interface=RedisSessionInterface(conn,key_prefix='lqz',permanent=False)
-设置cookie有效期
1、max_age:距离现在多少秒后过期,在IE8以下不支持
2、expires:datatime类型,使用此参数,需参照格林尼治时间,即北京时间-8个小时
3、如果max_age和expires都设置了,则以max_age为参准
4、若没有设置过期时间,则默认为浏览会话结束,即关闭浏览器(是关闭浏览器,不是关闭页面)时过期
@app.route('/')
def index():
res = make_response('hello')
res.set_cookie('name','wmt',max_age=10)
#此时就是设置的失效时间,代表10s失效,包括关闭浏览器也会有效,直到时间结束才会失效
retrun res
-设置session的过期时间
全局:在app.config里面配置
'PERMANENT_SESSION_LIFETIME'] = datetime.timedelta(minutes=1)
局部:给视图函数里面的配置session过期时间
#session的过期时间配置
from datetime import timedelta
from flask import session,Flask
app=Flask(__name__)
@app.before_request
def before_request():
#设置session过期时间,代表五分钟后过期
session.permanent = True
app.permanent_session_lifetime = timedelta(minutes=5)
二、数据库连接池
1.先安装包 pip install DBUtils
DBUtils提供两种连接模式:
PersistentDB:提供线程专用数据库连接,并自动管理连接
PooledDB:提供线程间可共享的数据库连接,并自动管理连接
import pymysql
from DBUtils.PooledDB import PooledDB
import time
from threading import Thread
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=[], # 开始会话前执行的命令列表。
ping=0, # ping MySQL服务端,检查是否服务可用。
host='127.0.0.1',
port=3306,
user='root',
password='123',
database='flask',
charset='utf8'
)
def func():
# 检测当前正在运行连接数的是否小于最大链接数,如果不小于则:等待或报raise TooManyConnections异常 # 否则 # 则优先去初始化时创建的链接中获取链接 SteadyDBConnection。 # 然后将SteadyDBConnection对象封装到PooledDedicatedDBConnection中并返回。 # 如果最开始创建的链接没有链接,则去创建一个SteadyDBConnection对象,再封装到PooledDedicatedDBConnection中并返回。 # 一旦关闭链接后,连接就返回到连接池让后续线程继续使用。
conn = POOL.connection() #做成一个单例,去池子拿数据 print(th, '链接被拿走了', conn1._con)
print(th, '池子里目前有', pool._idle_cache, '\r\n') cursor = conn.cursor()
cursor.execute('select * from boy')
result = cursor.fetchall()
time.sleep(2)
print(result)
conn.close()
if __name__ == '__main__':
for i in range(10):
t=Thread(target=func) #启动10条线程去拿
t.start()
上面def func()封装版的:
import pymysql
from settings import Config
class SQLHelper(object):
@staticmethod
def open(cursor):
POOL = Config.PYMYSQL_POOL
conn = POOL.connection()
cursor = conn.cursor(cursor=cursor)
return conn,cursor
@staticmethod
def close(conn,cursor):
conn.commit()
cursor.close()
conn.close()
@classmethod
def fetch_one(cls,sql,args,cursor=pymysql.cursors.DictCursor):
conn,cursor = cls.open(cursor)
cursor.execute(sql, args)
obj = cursor.fetchone()
cls.close(conn,cursor)
return obj
@classmethod
def fetch_all(cls,sql, args,cursor =pymysql.cursors.DictCursor):
conn, cursor = cls.open(cursor)
cursor.execute(sql, args)
obj = cursor.fetchall()
cls.close(conn, cursor)
return obj
@classmethod
def execute(cls,sql, args,cursor =pymysql.cursors.DictCursor):
conn, cursor = cls.open(cursor)
cursor.execute(sql, args)
cls.close(conn, cursor)
数据库连接池使用:建立池子pool、封装方法utils、是用脚本.py
pool.py
import pymysql
from DBUtils.PooledDB import PooledDB
import time
from threading import Thread
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=[], # 开始会话前执行的命令列表。
ping=0, # ping MySQL服务端,检查是否服务可用。
host='127.0.0.1',
port=3306,
user='root',
password='123',
database='flask',
charset='utf8'
)
utils
import pymysql
from pool import POOL #导入上面的数据库连接池
class SQLHelper(object):
@staticmethod
def open(cursor):
conn = POOL.connection()
cursor = conn.cursor(cursor=cursor)
return conn, cursor
@staticmethod
def close(conn, cursor):
conn.commit()
cursor.close()
conn.close()
@classmethod
def fetch_one(cls, sql, args, cursor=pymysql.cursors.DictCursor):
conn, cursor = cls.open(cursor)
cursor.execute(sql, args)
obj = cursor.fetchone()
cls.close(conn, cursor)
return obj
@classmethod
def fetch_all(cls, sql, args, cursor=pymysql.cursors.DictCursor):
conn, cursor = cls.open(cursor)
cursor.execute(sql, args)
obj = cursor.fetchall()
cls.close(conn, cursor)
return obj
@classmethod
def execute(cls, sql, args, cursor=pymysql.cursors.DictCursor):
conn, cursor = cls.open(cursor)
cursor.execute(sql, args)
cls.close(conn, cursor)
使用脚本.py
from flask import Flask,jsonify
from utils import SQLHelper
app = Flask(__name__)
#使用数据库连接池,继承上面utils写的类SQLHelper
@app.route('/')
def index():
res=SQLHelper.fetch_all(sql='select * from boy where id=%s',args=[1,])
return jsonify(res)
if __name__ == "__main__":
app.run()
三、wtforms(forms组件)
作用:1. 校验数据,2.渲染标签
使用:pip install wtforms
app.py
from flask import Flask,render_template,redirect,request
from wtforms import Form
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets
app = Flask(__name__,template_folder="templates")
app.debug = True
class RegisterForm(Form):
name = simple.StringField(
label="用户名",
validators=[
validators.DataRequired() #代表数据必须填
],
widget=widgets.TextInput(),
render_kw={"class":"form-control"}, #表示html属性
default="wd" #框里默认填的值
)
pwd = simple.PasswordField(
label="密码",
validators=[
validators.DataRequired(message="密码不能为空")
]
)
pwd_confim = simple.PasswordField(
label="重复密码",
validators=[
validators.DataRequired(message='重复密码不能为空.'),
validators.EqualTo('pwd',message="两次密码不一致")
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
email = html5.EmailField( #注意这里用的是html5.EmailField
label='邮箱',
validators=[
validators.DataRequired(message='邮箱不能为空.'),
validators.Email(message='邮箱格式错误')
],
widget=widgets.TextInput(input_type='email'),
render_kw={'class': 'form-control'}
)
gender = core.RadioField(
label="性别",
choices=(
(1,"男"),
(2,"女"),
),
coerce=int #限制是int类型的
)
city = core.SelectField(
label="城市",
choices=(
("bj","北京"),
("sh","上海"),
)
)
hobby = core.SelectMultipleField(
label='爱好',
choices=(
(1, '篮球'),
(2, '足球'),
),
coerce=int
)
favor = core.SelectMultipleField( #此时SelectMultipleField代表可以多选
label="喜好",
choices=(
(1, '篮球'),
(2, '足球'),
),
widget = widgets.ListWidget(prefix_label=False),
option_widget = widgets.CheckboxInput(),
coerce = int,
default = [1, 2]
)
def __init__(self,*args,**kwargs):
'''重写__init__方法'''
super(RegisterForm,self).__init__(*args, **kwargs)
self.favor.choices =((1, '篮球'), (2, '足球'), (3, '羽毛球')) #把RegisterForm这个类里面的favor重新赋值,实现动态改变复选框中的选项
def validate_pwd_confim(self,field,):
'''
自定义pwd_config字段规则
'''
# 最开始初始化时,self.data中已经有所有的值
if field.data != self.data['pwd']:
# raise validators.ValidationError("密码不一致") # 继续后续验证
raise validators.StopValidation("密码不一致") # 不再继续后续验证
@app.route('/register',methods=["GET","POST"])
def register():
if request.method=="GET":
form = RegisterForm(data={'gender': 1}) #默认是1,
return render_template("register.html",form=form)
else:
form = RegisterForm(formdata=request.form)
if form.validate(): #判断是否验证成功
print('用户提交数据通过格式验证,提交的值为:', form.data) #所有的正确信息
else:
print(form.errors) #所有的错误信息
return render_template('register.html', form=form)
if __name__ == '__main__':
app.run()
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户注册</h1>
<form method="post" novalidate style="padding:0 50px">
{% for item in form %}
<p>{{item.label}}: {{item}} {{item.errors[0] }}</p>
{% endfor %}
<input type="submit" value="提交">
</form>
</body>
</html>
四、信号(是一个同步操作)
1.使用:pip install blinker
2.flask内置信号参数:
request_started = _signals.signal('request-started') # 请求到来前执行
request_finished = _signals.signal('request-finished') # 请求结束后执行
before_render_template = _signals.signal('before-render-template') # 模板渲染前执行
template_rendered = _signals.signal('template-rendered') # 模板渲染后执行
got_request_exception = _signals.signal('got-request-exception') # 请求执行出现异常时执行
request_tearing_down = _signals.signal('request-tearing-down') # 请求执行完毕后自动执行(无论成功与否)
appcontext_tearing_down = _signals.signal('appcontext-tearing-down') # 应用上下文执行完毕后自动执行(无论成功与否)
appcontext_pushed = _signals.signal('appcontext-pushed') # 应用上下文push时执行
appcontext_popped = _signals.signal('appcontext-popped') # 应用上下文pop时执行
message_flashed = _signals.signal('message-flashed') # 调用flask在其中添加数据时,自动触发
3.如何使用:
内置信号的绑定
第一步写一个函数(触发某些动作)
往信号中注册函数
def func(*args,**kwargs):
print(args[0]) # 当前app对象
print('触发信号',args,kwargs)
第二步:函数跟内置信号绑定
signals.request_started.connect(func)
#函数与内置信号的绑定
from flask import Flask,signals
app = Flask(__name__)
#第一步写函数(触发某些动作)
def index(*args,**kwargs):
print('触发信号',args,kwargs)
#第二步:函数与信号绑定,请求来之前执行
signals.request_started.connect(index)
@app.route('/')
def login():
return "hello"
if __name__ == "__main__": app.run()
自定义信号的使用
自定义信号
第一步:定义一个信号
xxxxx = _signals.signal('xxxxx')
第二步:定义一个函数
def func3(*args,**kwargs):
import time
time.sleep(1)
print('触发信号',args,kwargs)
第三步:信号跟函数绑定
xxxxx.connect(func3)
第四步:触发信号
xxxxx.send(1,k='2')
from flask import Flask,signals
from flask.signals import _signals
app = Flask(__name__)
#第一步:自定义一个信号
xxxxx = _signals.signal("xxxxx") #尽量名字一样前后XXXX
#第二步定义函数
def index(*args,**kwargs):
print('触发信号',args,kwargs)
#第三步:函数与信号绑定
xxxxx.connect(index)
#第四步:触发信号
@app.route('/')
def login():
xxxxx.send(1) #触发信号,只能传一个位置,或者可以传一个字典
return "hello"
if __name__ == "__main__":
app.run()