Flask入门(三)之信号、MetaClass、Session、WTForms

上一篇文章>Flask入门(二)之偏函数、数据库连接池(DBUtils)

一、信号


1、安装blinker

Flask中默认不支持信号,需要我们安装第三方插件!!!
下载地址:https://pypi.org/project/blinker/#files

[root@Python ~]# wget https://files.pythonhosted.org/packages/1b/51/e2a9f3b757eb802f61dc1f2b09c8c99f6eb01cf06416c0671253536517b6/blinker-1.4.tar.gz
[root@Python ~]# tar xf blinker-1.4.tar.gz
[root@Python ~]# cd blinker-1.4/
[root@Python blinker-1.4]# python setup.py install

测试:

[root@Python ~]# python
Python 3.6.0 (default, Jun 17 2019, 16:17:21)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import blinker

2、内置信号

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、示例

from flask import Flask,signals
app = Flask(__name__)

### 往信号中注册函数
def fun(*args, **kwargs):
    print ('触发信号',args,kwargs)

### 触发信号:singnals.request_start.send()
signals.request_started.connect(fun)

@app.route('/', methods=['GET','POST'])
def index():
    print ('view')
    return 'index'

if __name__ == "__main__":
    app.run()

4、执行顺序


 1. before_first_request
 2. 触发request_started信号
 3. before_request
 4. 模板渲染
     渲染前的信号before_render_template.send(app,template=template, context=context)
         rv = template.render(context)    ###模板渲染
         渲染后的信号template_rendered_send(app,template=template, context=context)
 5. after_request
 6. session.save_session()
 7. 触发request_finished信号

 如果上述过程出错:
     触发错误处理信号got_request_exception.send(self,exception=e)
 8. 触发信号request_tearing_down

二、MetaClass


MetaClass作用:用来指定当前类由谁来创建(默认type创建)

1、MetaClass使用

(1)Python2
class Foo(object):
    __metaclass__ = type
    pass
(2)Python3
class Foo(metaclass=type):
    pass

2、三种操作方法

(1)普通用法
class MyType(type):
    def __init__(self,*args,**kwargs):
        print ("init")
        super(MyType,self).__init__(*args,**kwargs)

    ### call本质:调用类的__new__,再调用类的__init__
    def __call__(self, *args, **kwargs):
        print ('call')

class Foo(metaclass=MyType):
    pass

class Bar(Foo):
    pass
obj = Bar()

运行结果:
init
init
call
(2)升级用法

type可以创建类metaclass=type;MyType也可以创建类metaclass=MyType

class MyType(type):
    def __init__(self,*args,**kwargs):
        super(MyType,self).__init__(*args,**kwargs)

    ### call本质:调用类的__new__,再调用类的__init__
    def __call__(cls, *args, **kwargs):
        return super(MyType, cls).__call__(*args, **kwargs)

### Base = MyType('Base',(object,),{})
### MyType('Base',(object,),{})是由MyType创建;metaclass=MyType
class Foo(MyType('Base',(object,), {})):
    pass

obj = Foo()
(3)源码写法
class MyType(type):
    def __init__(self,*args,**kwargs):
        super(MyType,self).__init__(*args,**kwargs)

    def __call__(cls, *args, **kwargs):
        return super(MyType, cls).__call__(*args, **kwargs)

def with_metaclass(base):
    return MyType('xx',(base,), {})

class Foo(with_metaclass(object)):
    pass

obj = Foo()

三、flask-session


1、Flask的session处理机制

内置将session保存在加密cookie中实现

  • 请求到来:获取随机字符串,存在则取数据库中获取原来的个人数据,否则创建一个空容器。 -->内存:对象(随机字符串(放置数据的容器))
  • 视图:操作内存中的对象(随机字符串(放置数据的容器))
  • 响应:内存对象(随机字符串(放置数据的容器))
    • 将数据保存到数据库
    • 把随机字符串写到用户的cookie中

2、flask.config.Config参数介绍

flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
    {
        'DEBUG':                                get_debug_flag(default=False),  是否开启Debug模式
        'TESTING':                              False,                          是否开启测试模式
        'PROPAGATE_EXCEPTIONS':                 None,                          
        'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
        'SECRET_KEY':                           None,
        'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
        'USE_X_SENDFILE':                       False,
        'LOGGER_NAME':                          None,
        'LOGGER_HANDLER_POLICY':               'always',
        'SERVER_NAME':                          None,
        'APPLICATION_ROOT':                     None,
        'SESSION_COOKIE_NAME':                  'session',
        'SESSION_COOKIE_DOMAIN':                None,
        'SESSION_COOKIE_PATH':                  None,
        'SESSION_COOKIE_HTTPONLY':              True,
        'SESSION_COOKIE_SECURE':                False,
        'SESSION_REFRESH_EACH_REQUEST':         True,
        'MAX_CONTENT_LENGTH':                   None,
        'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
        'TRAP_BAD_REQUEST_ERRORS':              False,
        'TRAP_HTTP_EXCEPTIONS':                 False,
        'EXPLAIN_TEMPLATE_LOADING':             False,
        'PREFERRED_URL_SCHEME':                 'http',
        'JSON_AS_ASCII':                        True,
        'JSON_SORT_KEYS':                       True,
        'JSONIFY_PRETTYPRINT_REGULAR':          True,
        'JSONIFY_MIMETYPE':                     'application/json',
        'TEMPLATES_AUTO_RELOAD':                None,
    }

3、内置session

import os
from flask import Flask,session
app = Flask(__name__)
app.secret_key = os.urandom(24)
@app.route('/')
def index():
    ### 执行session对象的__setitem__方法
    session['xxx'] = 123
    return 'Index'

if __name__ == "__main__":
    app.run(host='10.10.10.111')

4、第三方插件Flask-Session

(1)Flask-Session安装

下载地址:https://pypi.org/project/Flask-Session/#files

[root@Python ~]# wget https://files.pythonhosted.org/packages/2e/9f/b138521d0416b001469bcdc79a3619f013c0563204f7251ba978eb3e69d5/Flask-Session-0.3.1.tar.gz
[root@Python ~]# tar xf Flask-Session-0.3.1.tar.gz
[root@Python ~]# cd Flask-Session-0.3.1/
[root@Python Flask-Session-0.3.1]# python setup.py install
(2)redis安装

<1> reidis-server安装

[root@python ~]# wget http://download.redis.io/releases/redis-4.0.8.tar.gz
[root@python ~]# yum install -y gcc
[root@python ~]# tar xf redis-4.0.8.tar.gz
[root@python redis-4.0.8]# cd redis-4.0.8
[root@python redis-4.0.8]# make && make install
[root@python redis-4.0.8]# cd utils/
[root@python utils]# ./install_server.sh          ###一直按回车即可

### 查看是否有端口
[root@python ~]# netstat -lntup|grep redis
tcp        0      0 127.0.0.1:6379          0.0.0.0:*               LISTEN      979/redis-server 12 
[root@Python ~]# vim /etc/redis/6379.conf
[root@Python ~]# /etc/init.d/redis_6379 restart

在这里插入图片描述
<2> python redis模块安装

[root@python ~]# wget https://files.pythonhosted.org/packages/4a/1b/9b40393630954b54a4182ca65a9cf80b41803108fcae435ffd6af57af5ae/redis-3.0.1.tar.gz
[root@python ~]# tar xf redis-3.0.1.tar.gz
[root@python ~]# cd redis-3.0.1/
[root@python redis-3.0.1]# python setup.py install

5、示例

(1)方式一
import os
from flask import Flask, session
from flask_session import RedisSessionInterface
from redis import Redis
app = Flask(__name__)
app.secret_key = os.urandom(24)
conn = Redis(host='10.10.10.111')
app.session_interface = RedisSessionInterface(conn, key_prefix='__', use_signer=False)
@app.route('/')
def index():
    session['xxx'] = 123
    return 'Index'

if __name__ == "__main__":
    app.run(host='10.10.10.111')
(2)方式二
import os
from flask import Flask, session
from redis import Redis
from flask_session import Session
app = Flask(__name__)
app.secret_key = os.urandom(24)
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = Redis(host='10.10.10.111')
Session(app)
@app.route('/')
def index():
    session['xxx'] = 123
    return 'Index'

if __name__ == "__main__":
    app.run(host='10.10.10.111')

四、WTForms


1、安装

下载地址:https://pypi.org/project/WTForms/#files

[root@Python ~]# wget https://files.pythonhosted.org/packages/cd/1d/7221354ebfc32b868740d02e44225c2ce00769b0d3dc370e463e2bc4b446/WTForms-2.2.1.tar.gz
[root@Python ~]# tar xf WTForms-2.2.1.tar.gz
[root@Python ~]# cd WTForms-2.2.1/
[root@Python WTForms-2.2.1]# python setup.py install

2、用户登录

(1)目录结构
[root@Python wtforms]# tree
.
├── app.py
└── templates
    └── login.html
(2)配置app.py
[root@Python wtforms]# vim app.py
#!/usr/bin/env python
#coding:utf-8
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

app = Flask(__name__, template_folder='templates')

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个数字和一个特殊字符')
        ],
        widget=widgets.PasswordInput(),
        render_kw={'class': 'form-control'}
    )

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

if __name__ == "__main__":
    app.run(host='10.10.10.111')
(3)配置HTML
[root@Python wtforms]# mkdir templates/
[root@Python wtforms]# vim templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登陆</h1>
<form method="POST">
    <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>

3、用户注册

(1)目录结构
[root@Python wtforms]# tree
.
├── app01.py
└── templates
    └── register.html
(2)配置app01.py
[root@Python wtforms]# vim app01.py
from flask import Flask, render_template, request, redirect
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'},
        default='Dream'
    )

    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 = 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
    )
    city = core.SelectField(
        label='城市',
        choices=(
            ('bj', '北京'),
            ('sh', '上海'),
        )
    )

    hobby = core.SelectMultipleField(
        label='爱好',
        choices=(
            (1, '篮球'),
            (2, '足球'),
        ),
        coerce=int
    )

    favor = core.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: 
        """
        ### 最开始初始化时,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})
        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(host='10.10.10.111')
(3)配置HTML
[root@Python wtforms]# vim templates/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>

下一篇文章>Flask入门(四)之SQLAlchemy、Flask-SQLAlchemy、pipreqs、Flask-Script、Flask-Migrate

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章