flask基础fourth,flask-session,数据库连接池,wyform组件,信号
一、 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里面配置
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()