前言:
Django:1個重武器,包含了web開發中經常使用的功能、組件的框架;(ORM、Session、Form、Admin、分頁、中間件、信號、緩存、ContenType....);css
Tornado:2大特性就是異步非阻塞、原生支持WebSocket協議;html
Flask:封裝功能不及Django完善,性能不及Tornado,可是Flask的第三方開源組件比豐富;http://flask.pocoo.org/extensions/前端
Bottle:比較簡單;python
總結:web
都不是我寫的!!!不論優劣,不一樣的工具而已;正則表達式
小型web應用設計的功能點很少使用Flask;redis
大型web應用設計的功能點比較多使用的組件也會比較多,使用Django(自帶功能多不用去找插件);sql
若是追求性能能夠考慮Tornado;數據庫
Flask的socket是基於Werkzeug 實現的,模板語言依賴jinja2模板,在使用Flask以前須要安裝一下;json
pip3 install flask #安裝flask
![](http://static.javashuo.com/static/loading.gif)
Flask簡單使用
![](http://static.javashuo.com/static/loading.gif)
1、配置文件
app=Flask(__name__,template_folder='templates',static_url_path='/static/',static_path='/zhanggen')
模板路徑: template_folder='templates'
靜態文件路徑:static_url_path='/static/'
靜態文件引入別名:static_path='/zhanggen'
設置爲調試環境:app.debug=True (代碼修改自動更新)
設置json編碼格式 若是爲False 就不使用ascii編碼:app.config['JSON_AS_ASCII']=False
設置響應頭信息Content-Type app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8" (注意 ;charset=utf-8)
2、路由系統
1.動態路由(url傳參)
@app.route('/user/<name>')
![](http://static.javashuo.com/static/loading.gif)
@app.route('/post/<int:age>')
![](http://static.javashuo.com/static/loading.gif)
@app.route('/post/<float:salary>')
![](http://static.javashuo.com/static/loading.gif)
@app.route('/post/<path:path>')
![](http://static.javashuo.com/static/loading.gif)
二、指定容許的請求方法
@app.route('/login', methods=['GET', 'POST'])
![](http://static.javashuo.com/static/loading.gif)
三、經過別名反向生成url
![](http://static.javashuo.com/static/loading.gif)
四、經過app.add_url_rule()調用路由
![](http://static.javashuo.com/static/loading.gif)
五、擴展路由功能:正則匹配url
若是須要一些複雜的匹配規則能夠自定義正則匹配url
![](http://static.javashuo.com/static/loading.gif)
4、視圖
一、給Flask視圖函數加裝飾器
注意若是要給視圖函數加裝飾器增長新功能,一點要加在路由裝飾器下面,纔會被路由裝飾器裝飾,才能生生成url關係;
![](http://static.javashuo.com/static/loading.gif)
二、request和response
a.請求相關信息
request.method: 獲取請求方法
request.json
request.json.get("json_key"):獲取json數據 **較經常使用
request.argsget('name') :獲取get請求參數
request.form.get('name') :獲取POST請求參數
request.form.getlist('name_list'):獲取POST請求參數列表(多個)
request.values.get('age') :獲取GET和POST請求攜帶的全部參數(GET/POST通用)
request.cookies.get('name'):獲取cookies信息
request.headers.get('Host'):獲取請求頭相關信息
request.path:獲取用戶訪問的url地址,例如(/,/login/,/ index/);
request.full_path:獲取用戶訪問的完整url地址+參數 例如(/login/?age=18)
request.script_root: 抱歉,暫未理解其含義;
request.url:獲取訪問url地址,例如http://127.0.0.1:5000/?age=18;
request.base_url:獲取訪問url地址,例如 http://127.0.0.1:5000/;
request.url_root
request.host_url
request.host:獲取主機地址
request.files:獲取用戶上傳的文件
obj = request.files['the_file_name']
obj.save('/var/www/uploads/' + secure_filename(f.filename)) 直接保存
b、響應相關信息
return "字符串" :響應字符串
return render_template('html模板路徑',**{}):響應模板
return redirect('/index.html'):跳轉頁面
響應json數據
方式1: return jsonify(user_list)
![](http://static.javashuo.com/static/loading.gif)
方式2:
return Response(data,mimetype="application/json;charset=utf-8",)
若是須要設置響應頭就須要藉助make_response()方法
from flask import Flask,request,make_response
response = make_response(render_template('index.html'))
response是flask.wrappers.Response類型
response.delete_cookie('key')
response.set_cookie('key', 'value')
response.headers['X-Something'] = 'A value'
return respons
3 、Flask之CBV視圖
![](http://static.javashuo.com/static/loading.gif)
5、模板語言
Flask使用的是Jinja2模板,因此其語法和Django無差異(Django的模板語言參考Jinja2)
1.引用靜態文件
方式1:別名引入
<link rel="stylesheet" href="/zhanggen/commons.css">
方式2:url_for()方法引入
<link rel="stylesheet" href="{{ url_for('static',filename='commons.css') }}">
2.模板語言引用上下文對象
變量
![](http://static.javashuo.com/static/loading.gif)
循環、索引取值
![](http://static.javashuo.com/static/loading.gif)
Flask的Jinjia2能夠經過Context 把視圖中的函數傳遞把模板語言中執行,這就是Django中的simple_tag和simple_fifter;
simple_tag(只能傳2個參數,支持for、if)
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
simple_fifter(對參數個數無限制,不支持for、if)
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
3.wtform(flask表單驗證插件)
3.0.簡介
wtforms WTForms是一個支持多個web框架的form組件,主要對用戶請求數據 進行表單驗證。
3.1. 安裝
pip install wtforms #安裝wtfroms插件
3.2.簡單使用
wtforms和Django自帶的form驗證插件功能相同,使用起來大同小異;
用戶登陸頁面驗證
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
用戶註冊頁面驗證
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
3.2.wtforms源碼 猜測....
A.自動生成html標籤
先來分析一下form驗證類的結構
LoginForm類中包含了2個字段: name 和 pwd,而name / pwd字段 = 對象,因此LoginForm 類包含了2個對象;
若是實例化了obj=LoginForm() 就等於 在 這1個對象中嵌套了 2個對象;
前端使用Form驗證插件:
那若是在前端for循環LoginForm對象,就等於調用LoginForm對象的__iter__方法,把每一個字段(對象)封裝的數據 返回
若是前端{{ obj }}= 直接調用了字段對象的__str__方法;
![](http://static.javashuo.com/static/loading.gif)
B.數據校驗
後臺定義好正則
用戶發來數據
對數據進行校驗
3.3.源碼流程
生成HTML標籤並顯示
1.驗證類(LogibForm)生成
1.1.因爲 metaclass=FormMeta,因此LoginForm是由FormMeta建立的
![](http://static.javashuo.com/static/loading.gif)
1.2.執行FormMeta 的__init__方法,在LoginForm中添加2個靜態字段
![](http://static.javashuo.com/static/loading.gif)
1.3.開始解釋LoginForm中的 實例化字段對象name=simple.StringField()simple.PasswordField()
StringField/PasswordField開始實例化(提到實例化就應該想到:指定元類的__call__、本身/父類的__new__、__init__):
StringField/PasswordField是默認元類,本身沒有__new__和__init__方法;
但父類Field類中有__new__方法,因此執行父類的__new__(Field.__new__)返回UnboundField對象
![](http://static.javashuo.com/static/loading.gif)
因爲Field.__new__方法返回了 1個 UnboundField對象,來看 UnboundField的__init__方法
![](http://static.javashuo.com/static/loading.gif)
UnboundField的__init__方法在 UnboundField對象中封裝了Field類的參數和計數器,因此如今LoginForml類中封裝數據以下
""" print(LoginForm.__dict__) LoginForm ={ '__module__': '__main__', 'name': <1 UnboundField(StringField, (),{'creation_counter': 1, 'label': '用戶名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'} })>, 'pwd': <2 UnboundField(PasswordField, (),{'creation_counter': 2,'label': '密碼', 'validators': [<wtforms.validators.DataRequired object at 0x000000000382B0F0>, <wtforms.validators.Length object at 0x000000000382B128>, <wtforms.validators.Regexp object at 0x000000000382B160>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x000000000382B208>, 'render_kw': {'class': 'form-control'}})>, '__doc__': None, '_unbound_fields': None, '_wtforms_meta': None, } """
啓發:
不必定要把代碼都寫在當前類中,如過多個類和類之間有同性方法、屬性能夠抽出來集中到父類之中;子類繼承父類因此子類實例化對象以後,繼承享有2者的屬性和方法;因此看源碼遇到繼承一點要注意 觀察父類;
每一個對象實例化(在排除MetaClass的狀況下)都會執行 父類的__new__方法,再去執行__init__方法;而__new__實質性決定了實例化出來的對象是神馬?
![](http://static.javashuo.com/static/loading.gif)
2.LoginForm實例化
談到類實例化應該先檢查該類是否指定了 Meta類,若是指定了Meta類, 就須要先執行 (指定元類的__call__、本身/父類的__new__、__init__)
21.執行FormMeta的__call__方法,賦值LoginForm的_unbound_fields 和 _wtforms_meta屬性;
根據unbound對象的creation_counter屬性對 LoginForm中的字段進行排序,並填充到 LoginForm的_unbound_fields屬性中
根據 LoginForm的__mro__繼承順序:獲取當前類(FormLogin)全部父類,並在每一個父類中 提取Meta屬性添加到列表,轉成元組,最後建立Meta類讓其繼承,賦值LoginForm._wtforms_meta屬性
![](http://static.javashuo.com/static/loading.gif)
執行完了指定元類 FormMeta.__call__()方法以後的LoginForm類中封裝的數據
print(LoginForm.__dict__) LoginForm ={ '__module__': '__main__', 'name': <1 UnboundField(StringField, (),{'creation_counter': 1, 'label': '用戶名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'} })>, 'pwd': <2 UnboundField(PasswordField, (),{'creation_counter': 2,'label': '密碼', 'validators': [<wtforms.validators.DataRequired object at 0x000000000382B0F0>, <wtforms.validators.Length object at 0x000000000382B128>, <wtforms.validators.Regexp object at 0x000000000382B160>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x000000000382B208>, 'render_kw': {'class': 'form-control'}})>, '__doc__': None, '_unbound_fields': [ (name, UnboundField對象(1,simple.StringField,參數)), (pwd, UnboundField對象(2,simple.PasswordField,參數)), ],, '_wtforms_meta': Meta(DefaultMeta)類, } """
啓發:
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
2.2.執行LoginForm的__new__方法
沒有__new__方法 pass
2.3.執行LoginForm的__init__方法實例化form對象
![](http://static.javashuo.com/static/loading.gif)
執行Form父類BaseForm.__init__方法,把UnboundField對象轉換成StringField對象,並賦值到form對象的_fields:{}字典中;
![](http://static.javashuo.com/static/loading.gif)
form = { _fields: { name: StringField對象(), pwd: PasswordField對象(), }
循環form對象 中的_fields字段(字典),分別賦值到form對象,這樣就能夠經過form.name/form.pwd直接獲取到Field對象了
,無需form._fields['name'] / form._fields['name']
代碼:
for name, field in iteritems(self._fields): setattr(self, name, field)
form對象封裝數據就變成如下內容嘍
form = { _fields: { name: StringField對象(), pwd: PasswordField對象(), } name: StringField對象(widget=widgets.TextInput()), pwd: PasswordField對象(widget=widgets.PasswordInput()) }
3. 當form對象生成以後 print(form.name) = 執行StringField對象的__str__方法;
StringField類中沒有__str__方法,因此去執行基類Field的,Field.__str__方法返回了: self() = StringFieldObj.__call__()
![](http://static.javashuo.com/static/loading.gif)
StringField沒有__call__因此執行其基類Field.__call__方法,調用了self.meta.render_field(self, kwargs)
def __call__(self, **kwargs): # self=StringField對象 return self.meta.render_field(self, kwargs) #把StringField對象傳傳入meta.render_field方法
下面來看self.meta.render_field(self, kwargs)作了什麼?
def render_field(self, field, render_kw): other_kw = getattr(field, 'render_kw', None) if other_kw is not None: render_kw = dict(other_kw, **render_kw) # StringField對象.widget(field, **render_kw) #插件.__call__() ''' #field =StringField對象 StringField對象.widget對象()=調用widget對象的.__call__方法 ''' return field.widget(field, **render_kw)
來看widget對象=TextInput()的__call__方法,最終打印了obj.name的結果
def __call__(self, field, **kwargs): kwargs.setdefault('id', field.id) kwargs.setdefault('type', self.input_type) if 'value' not in kwargs: kwargs['value'] = field._value() if 'required' not in kwargs and 'required' in getattr(field, 'flags', []): kwargs['required'] = True return HTMLString('<input %s>' % self.html_params(name=field.name, **kwargs))
""" 0. Form.__iter__: 返回全部字段對象 1. StringField對象.__str__ 2. StringField對象.__call__ 3. meta.render_field(StringField對象,) 4. StringField對象.widget(field, **render_kw) 5. 插件.__call__() """
4.執行for iteam in form對象的執行流程
執行form對象基類BaseForm的__inter__方法,變量self._fields字典中的內容
def __iter__(self): """Iterate form fields in creation order.""" return iter(itervalues(self._fields))
_fields: { name: StringField對象(), pwd: PasswordField對象(), }
用戶輸入數據的校驗驗證流程form = LoginForm(formdata=request.form)
# 請求發過來的值 form = LoginForm(formdata=request.form) # 值.getlist('name') # 實例:編輯 # # 從數據庫對象 # form = LoginForm(obj='值') # 值.name/值.pwd # # # 字典 {} # form = LoginForm(data=request.form) # 值['name'] # 1. 循環全部的字段 # 2. 獲取每一個字段的鉤子函數 # 3. 爲每一個字段執行他的驗證流程 字段.validate(鉤子函數+內置驗證規則)
6、session功能
1. Flask自帶的session功能
![](http://static.javashuo.com/static/loading.gif)
2.第三方session組件(Session)
安裝 pip install flask-session
![](http://static.javashuo.com/static/loading.gif)
不只能夠把session存放到redis還可放到文件、內存、memcache...
![](http://static.javashuo.com/static/loading.gif)
3.自定義session組件
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
7、藍圖
使用Flask自帶Blueprintmuk模塊,幫助咱們作代碼目錄結構的歸類
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
8、message (閃現)
message是一個基於Session實現的用於保存數據的集合,其特色是:一次性。
特色:和labada匿名函數同樣不長期佔用內存
![](http://static.javashuo.com/static/loading.gif)
9、中間件
flask也有中間件功能和Django相似,不一樣的是使用的是使用3個裝飾器來實現的;
1.@app.before_first_request :請求第1次到來執行1次,以後都不執行;
2.@app.before_request:請求到達視圖以前執行;(改函數不能有返回值,不然直接在當前返回)
3.@app.after_request:請求 通過視圖以後執行;(最下面的先執行)
![](http://static.javashuo.com/static/loading.gif)
10、Flask相關組件
二、flask-script組件
flask-script組件:用於經過腳本的形式,啓動 flask;(實現相似Django的python manager.py runserver 0.0.0.0:8001)
pip install flask-script #安裝
![](http://static.javashuo.com/static/loading.gif)
python run.py runserver -h 0.0.0.0 -p 8001
* Running on http://0.0.0.0:8001/ (Press CTRL+C to quit)
3.flask-migrate組件
在線修改、遷移數據庫(Django的 migrate 。
![](http://static.javashuo.com/static/loading.gif)
pip install flask-migrate #安裝
3.1.初始化數據庫:python run.py db init
3.2.遷移數據: python run.py db migrate
3.3.生成表: python run.py db upgrade
ps:修改表結構 first 直接註釋靜態字段代碼,second 執行 python run.py db upgrade.
D:\Flask練習\sansa>python run.py db init Creating directory D:\Flask練習\sansa\migrations ... done Creating directory D:\Flask練習\sansa\migrations\versions ... done Generating D:\Flask練習\sansa\migrations\alembic.ini ... done Generating D:\Flask練習\sansa\migrations\env.py ... done Generating D:\Flask練習\sansa\migrations\README ... done Generating D:\Flask練習\sansa\migrations\script.py.mako ... done Please edit configuration/connection/logging settings in 'D:\\Flask練習\\sansa\\migrations\\alembic.ini' before proceeding. D:\Flask練習\sansa>python run.py db migrate INFO [alembic.runtime.migration] Context impl MySQLImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.autogenerate.compare] Detected added table 'users666' Generating D:\Flask練習\sansa\migrations\versions\a7f412a8146f_.py ... done D:\Flask練習\sansa>python run.py db upgrade INFO [alembic.runtime.migration] Context impl MySQLImpl. INFO [alembic.runtime.migration] Will assume non-transactional DDL. INFO [alembic.runtime.migration] Running upgrade -> a7f412a8146f, empty message D:\Flask練習\sansa>