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()

 

posted @ 2022-03-23 11:15  新入世界的小白  阅读(58)  评论(0编辑  收藏  举报