上一篇文章
>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