内容概览
g对象 flask-session使用 数据库连接池 wtforms 信号
g对象
- 其实是请求的上下文,从请求进来,就有,到请求走了,一直存在,所以在当次请求过程中,如果调用别的函数,不需要把参数传入,只需要放到g对象中,在别的函数中直接使用g获取即可
@app. route ( '/' )
def index ( ) :
g. name = 'xxx'
print ( g. get( 'name' ) )
return 'index'
@app. route ( '/home' )
def home ( ) :
print ( g. get( 'name' ) )
return 'home'
request. context
担心把requets原来的属性替换掉
- g只针对于当次请求
- session针对于所有请求
flask-session使用
"""
flask内置的session,把数据加密后保存到浏览器了
能不能自己写个session的类,重写open_session和save_session,把数据保存到服务端的redis中
第三方:flask-session已经把这事干了,可以放到文件,redis,mongodb,关系型数据库
"""
pip install flask- session
from flask import Flask, session
from flask_session. sessions import RedisSessionInterface
from redis import Redis
app = Flask( __name__)
app. debug = True
conn = Redis( host= '127.0.0.1' , port= 6379 )
app. session_interface = RedisSessionInterface( redis= conn, key_prefix= 'aaa' )
@app. route ( '/' )
def index ( ) :
session[ 'name' ] = 'xxx'
return 'index'
if __name__ == '__main__' :
app. run( host= '127.0.0.1' , port= 8000 )
from flask import Flask, session
from flask_session import Session
app = Flask( __name__)
app. debug = True
app. config. from_pyfile( 'setting.py' )
Session( app)
@app. route ( '/' )
def index ( ) :
session[ 'name' ] = 'xxx'
return 'index'
if __name__ == '__main__' :
app. run( host= '127.0.0.1' , port= 8000 )
"""
方案二源码分析
本质在 Session(app)--->就是根据配置文件,生成 RedisSessionInterface 对象,赋值给app.session_interface
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
"""
数据库连接池
flask中集成mysql
import pymysql
from flask import Flask, jsonify
app = Flask( __name__)
@app. route ( '/' )
def index ( ) :
conn = pymysql. connect( host= '127.0.0.1' , port= 3306 , database= 'cnblogs' , user= 'root' , password= '123' )
cursor = conn. cursor( )
cursor. execute( 'select * from article' )
res = cursor. fetchall( )
cursor. close( )
conn. close( )
print ( res)
return jsonify( res)
if __name__ == '__main__' :
app. run( host= '127.0.0.1' , port= 8080 )
上面代码存在的问题
- django就是这么做的
- 每个视图函数使用同一个cursor, 这样会错乱
使用数据库连接池
pip install DBUtils
import pymysql
from dbutils. pooled_db import PooledDB
POOL = PooledDB(
creator= pymysql,
maxconnections= 6 ,
mincached= 2 ,
maxcached= 5 ,
maxshared= 0 ,
blocking= True ,
maxusage= None ,
setsession= [ ] ,
ping= 0 ,
host= '127.0.0.1' ,
port= 3306 ,
user= 'root' ,
password= '123' ,
database= 'cnblogs' ,
charset= 'utf8'
)
from db import POOL
@app. route ( '/' )
def index ( ) :
conn = POOL. connection( )
cursor = conn. cursor( )
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( )
show status like 'Threads%'
wtforms
1 校验数据
2 渲染错误信息
3 渲染页面
pip3 install wtfroms
python
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms. fields import simple
from wtforms import validators
from wtforms import widgets
from wtforms. fields import choices
app = Flask( __name__, template_folder= 'templates' )
app. debug = True
class LoginForm ( Form) :
name = simple. StringField(
label= '用户名' ,
validators= [
validators. DataRequired( message= '用户名不能为空.' ) ,
validators. Length( min = 6 , max = 18 , message= '用户名长度必须大于%(min)d且小于%(max)d' )
] ,
widget= widgets. TextInput( ) ,
render_kw= { 'class' : 'form-control' }
)
pwd = simple. PasswordField(
label= '密码' ,
validators= [
validators. DataRequired( message= '密码不能为空.' ) ,
validators. Length( min = 8 , message= '用户名长度必须大于%(min)d' ) ,
validators. Regexp( regex= "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}" ,
message= '密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符' )
] ,
widget= widgets. PasswordInput( ) ,
render_kw= { 'class' : 'form-control' }
)
class RegisterForm ( Form) :
name = simple. StringField(
label= '用户名' ,
validators= [
validators. DataRequired( )
] ,
widget= widgets. TextInput( ) ,
render_kw= { 'class' : 'form-control' } ,
default= 'pyy'
)
pwd = simple. PasswordField(
label= '密码' ,
validators= [
validators. DataRequired( message= '密码不能为空.' )
] ,
widget= widgets. PasswordInput( ) ,
render_kw= { 'class' : 'form-control' }
)
pwd_confirm = simple. PasswordField(
label= '重复密码' ,
validators= [
validators. DataRequired( message= '重复密码不能为空.' ) ,
validators. EqualTo( 'pwd' , message= "两次密码输入不一致" )
] ,
widget= widgets. PasswordInput( ) ,
render_kw= { 'class' : 'form-control' }
)
email = simple. EmailField(
label= '邮箱' ,
validators= [
validators. DataRequired( message= '邮箱不能为空.' ) ,
validators. Email( message= '邮箱格式错误' )
] ,
widget= widgets. TextInput( input_type= 'email' ) ,
render_kw= { 'class' : 'form-control' }
)
gender = choices. RadioField(
label= '性别' ,
choices= (
( 1 , '男' ) ,
( 2 , '女' ) ,
) ,
coerce = int
)
city = choices. SelectField(
label= '城市' ,
choices= (
( 'bj' , '北京' ) ,
( 'sh' , '上海' ) ,
)
)
hobby = choices. SelectMultipleField(
label= '爱好' ,
choices= (
( 1 , '篮球' ) ,
( 2 , '足球' ) ,
) ,
coerce = int
)
favor = choices. 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) :
super ( RegisterForm, self) . __init__( * args, ** kwargs)
self. favor. choices = ( ( 1 , '篮球' ) , ( 2 , '足球' ) , ( 3 , '羽毛球' ) )
def validate_pwd_confirm ( self, field) :
"""
自定义pwd_confirm字段规则,例:与pwd字段是否一致
:param field:
:return:
"""
if field. data != self. data[ 'pwd' ] :
raise validators. StopValidation( "密码不一致" )
@app. route ( '/login' , methods= [ 'GET' , 'POST' ] )
def login ( ) :
if request. method == 'GET' :
form = LoginForm( )
return render_template( 'login.html' , form= form)
else :
form = LoginForm( formdata= request. form)
if form. validate( ) :
print ( '用户提交数据通过格式验证,提交的值为:' , form. data)
else :
print ( form. errors)
return render_template( 'login.html' , form= form)
@app. route ( '/register' , methods= [ 'GET' , 'POST' ] )
def register ( ) :
if request. method == 'GET' :
form = RegisterForm( data= { 'gender' : 2 , 'hobby' : [ 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( )
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 field in form %}
< p> {{field.label}}: {{field}} {{field.errors[0] }}</ p>
{% endfor %}
< input type = " submit" value = " 提交" >
</ form>
</ body>
</ html>
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> Title</ title>
</ head>
< body>
< h1> 登录</ h1>
< form method = " post" novalidate >
< p> {{form.name.label}} {{form.name}} {{form.name.errors[0] }}</ p>
< p> {{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</ p>
< input type = " submit" value = " 提交" >
</ form>
</ body>
</ html>
信号
"""
信号和信号量
信号量(Semaphore):信号量可以理解为多把锁,同时允许多个线程来更改数据
信号(signal)Flask框架中的信号基于blinker,其主要就是让开发者可是在flask请求过程中定制一些用户行为
"""
- 代码耦合性低
- before_render_template :只要模板渲染就会执行,记录日志,login页面被渲染了多少次
- 统计今天用户访问量
- User表只要删除记录,就干个什么事
- 向banner表存数据,双写一致性问题:定时更新
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' )
message_flashed = _signals. signal( 'message-flashed' )
1. 定义函数
def before_render ( ) :
print ( 'before_render' )
2. 与内置信号绑定
from flask import signals
signals. before_render_template. connect( before_render)
3. 等待信号被触发
1. 定义一个自定义信号
from flask. signals import _signals
aaa = _signals. signal( 'aaa' )
2. 定义函数
3. 绑定自定义的信号
aaa. connect( func)
4. 触发自定义的信号
@app. route ( '/' )
def index ( ) :
aaa. send( )
return 'index'
"""
遇到的报错:RuntimeError: Signalling support is unavailable because the blinker library is not installed.
解决方法:没有安装blinker,安装即可 pip install blinker
"""