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

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