前言:
Django:1個重武器,包含了web開發中經常使用的功能、組件的框架;(ORM、Session、Form、Admin、分頁、中間件、信號、緩存、ContenType....);css
Tornado:2大特性就是異步非阻塞、原生支持WebSocket協議;html
Flask:封裝功能不及Django完善,性能不及Tornado,可是Flask的第三方開源組件比豐富;http://flask.pocoo.org/extensions/前端
Bottle:比較簡單;html5
總結:python
都不是我寫的!!!不論優劣,不一樣的工具而已;web
小型web應用設計的功能點很少使用Flask;正則表達式
大型web應用設計的功能點比較多使用的組件也會比較多,使用Django(自帶功能多不用去找插件);redis
若是追求性能能夠考慮Tornado;sql
Flask的socket是基於Werkzeug 實現的,模板語言依賴jinja2模板,在使用Flask以前須要安裝一下;mongodb
pip3 install flask #安裝flask
from werkzeug.wrappers import Request, Response # Flask的socket使用werkzeug實現,因此要導入 werkzeug@Request.application def hellow(request):return Response('Hello World') if__name__ == '__main__':from werkzeug.serving import run_simple run_simple('localhost',400,hellow)
Flask簡單使用
from flask import Flask app=Flask(__name__) #建立1個Flask實例@app.route('/') #路由系統生成 視圖對應url,1. decorator=app.route() 2. decorator(first_flask)def first_flask(): #視圖函數return'Hello World'#response if__name__ == '__main__': app.run() #啓動socket
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>')
from flask import Flask app=Flask(__name__) @app.route('/<name>') #設置url傳參數 http://127.0.0.1:5000/zhanggendef first_flask(name): #視圖必須有對應接收參數print(name)return'Hello World'#response if__name__ == '__main__': app.run()
@app.route('/post/<int:age>')
#接收整型數字參數app=Flask(__name__)@app.route('/<int:age>/') #設置url傳參數 http://127.0.0.1:5000/18/def first_flask(age): #視圖必須有對應接收參數print(age)return'Hello World'#response if__name__ == '__main__': app.run()
@app.route('/post/<float:salary>')
#接收浮點型型數字參數app=Flask(__name__)@app.route('/<float:salary>/') #設置url傳參數http://127.0.0.1:5000/2345555.8889/def first_flask(salary): #視圖必須有對應接收參數print(salary)return'Hello World'#response if__name__ == '__main__': app.run()
@app.route('/post/<path:path>')
# 接收URL連接類型參數app=Flask(__name__)@app.route('/<path:url>/') #設置url傳參數:http://127.0.0.1:5000/http://www.baiu.com/def first_flask(url): #視圖必須有對應接收參數print(url)return'Hello World'#response if__name__ == '__main__': app.run()
二、指定容許的請求方法
@app.route('/login', methods=['GET', 'POST'])
# 指定容許的請求方法app=Flask(__name__)@app.route('/<path:url>/',methods=['get']) #只容許get請求def first_flask(url):print(url)return'Hello World'#response if__name__ == '__main__': app.run()
三、經過別名反向生成url
#反向生成urlfrom flask import Flask,url_forapp=Flask(__name__)@app.route('/<path:url>',endpoint='name1')def first_flask(url):print(url_for('name1',url=url)) #若是設置了url參數,url_for(別名,加參數)return'Hello World' if__name__ == '__main__': app.run()
四、經過app.add_url_rule()調用路由
#方式2經過app.add_url_rule()方法的方式調用路由app=Flask(__name__) def first_flask():return'Hello World' app.add_url_rule(rule='/index/',endpoint='name1',view_func=first_flask,methods=['GET'])#app.add_url_rule(rule=訪問的url,endpoint=路由別名,view_func=視圖名稱,methods=[容許訪問的方法])if__name__ == '__main__': app.run()
五、擴展路由功能:正則匹配url
若是須要一些複雜的匹配規則能夠自定義正則匹配url
from flask import Flask, views, url_forfrom werkzeug.routing import BaseConverter app = Flask(import_name=__name__) class RegexConverter(BaseConverter):""" 自定義URL匹配正則表達式"""def__init__(self, map, regex): super(RegexConverter, self).__init__(map) self.regex = regex def to_python(self, value):""" 路由匹配時,匹配成功後傳遞給視圖函數中參數的值 :param value: :return: """return int(value) def to_url(self, value):""" 使用url_for反向生成URL時,傳遞的參數通過該方法處理,返回的值用於生成URL中的參數 :param value: :return: """ val = super(RegexConverter, self).to_url(value)return val # 添加到flask中 app.url_map.converters['regex'] = RegexConverter @app.route('/index/<regex("\d+"):nid>')def index(nid):print(url_for('index', nid='888'))return'Index' if__name__ == '__main__': app.run()
4、視圖
一、給Flask視圖函數加裝飾器
注意若是要給視圖函數加裝飾器增長新功能,一點要加在路由裝飾器下面,纔會被路由裝飾器裝飾,才能生生成url關係;
#給Flask視圖加裝飾器#一、定義1個裝飾器 def auth(func):print('我在上面')def inner(*args,**kwargs):return func(*args,**kwargs)return inner app=Flask(__name__) @app.route('/',methods=['GET'])@auth #注意若是要給視圖函數加裝飾器,一點要加在路由裝飾器下面,纔會被路由裝飾器裝飾def first_flask():print('ffff')return'Hello World' if__name__ == '__main__': app.run()
二、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)
app.config['JSON_AS_ASCII']=False #指定json編碼格式 若是爲False 就不使用ascii編碼,app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8"#指定瀏覽器渲染的文件類型,和解碼格式;
方式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視圖
#CBV視圖from flask import Flask,url_for,views#-----------------------------------------------------app=Flask(__name__) #裝飾器 def auth(func):print('我在上面')def inner(*args,**kwargs):return func(*args,**kwargs)return inner#--------------------------------------------------------class IndexView(views.MethodView): #CBV視圖 methods=['GET'] #容許的http請求方法(改CBV只容許GET方法) decorators = [auth,] #每次請求過來都加auth裝飾器 def get(self):return'Index.GET'def post(self):return'Index.POST' app.add_url_rule('/index/',view_func=IndexView.as_view(name='name1')) #(name='name1'反向生成url別名 if__name__ == '__main__': app.run()
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.模板語言引用上下文對象
變量
<h1>{{user_list}}</h1> <!--變量 -->
循環、索引取值
<ul>{% for user in user_list %} <!--循環 --> <li>{{user}}</li>{% endfor %} {{user_list.0}} <!-- 索引取值--> </ul>
Flask的Jinjia2能夠經過Context 把視圖中的函數傳遞把模板語言中執行,這就是Django中的simple_tag和simple_fifter;
simple_tag(只能傳2個參數,支持for、if)
@app.template_global() #simple_tagdef foo(arg):return'<input type="text">'
<h1>{{foo(1)|safe}}</h1><!--Flask的模板語言支持simple_tag-->
simple_fifter(對參數個數無限制,不支持for、if)
@app.template_filter() #simple_fifterdef foo1(arg1,arg2,arg3):return arg1+arg2+arg3
<h1> {{ 'alex'|foo1('s ','b',) }} </h1><!-- simple_fifter -->
3.wtform(flask表單驗證插件)
3.0.簡介
wtforms WTForms是一個支持多個web框架的form組件,主要對用戶請求數據 進行表單驗證。
3.1. 安裝
pip install wtforms #安裝wtfroms插件
3.2.簡單使用
wtforms和Django自帶的form驗證插件功能相同,使用起來大同小異;
用戶登陸頁面驗證
#!/usr/bin/env python# -*- coding:utf-8 -*-from flask import Flask, render_template, request, redirectfrom wtforms import Formfrom wtforms.fields import corefrom wtforms.fields import html5from wtforms.fields import simplefrom wtforms import validatorsfrom wtforms import widgets app=Flask(__name__,template_folder='templates') #知道模板文件app.debug=True #登陸驗證明例class LoginForm(Form): #不一樣的字段 內部包含正則表達式 html5.EmailField | html5.DateTimeField... name=simple.StringField( label='用戶名', validators=[ #驗證規則和錯誤提示信息 validators.DataRequired(message='用戶名不能爲空.'), validators.Length(min=6, max=18, message='用戶名長度必須大於%(min)d且小於%(max)d') ], widget=widgets.TextInput(), #前端頁面顯示的插件.TextArea render_kw={'class': 'form-control'} #設置form標籤的class信息 ) # 不一樣的字段 內部包含正則表達式 html5.EmailField | html5.DateTimeField... 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個數字和1個特殊字符') ], widget=widgets.PasswordInput(), render_kw={'class': 'form-control'} ) @app.route('/login/', methods=['GET', 'POST'])def login():if request.method == 'GET': form = LoginForm() #實例化 form驗證類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()
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><h1>登陸</h1><form method="post" novalidate><!--<input type="text" name="name">--><p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p> <!--<input type="password" name="pwd">--><p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p><input type="submit" value="提交"></form></body></html> login.html
用戶註冊頁面驗證
#用戶註冊from flask import Flask, render_template, request, redirectfrom wtforms import Formfrom wtforms.fields import corefrom wtforms.fields import html5from wtforms.fields import simplefrom wtforms import validatorsfrom 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='張根'#設置input標籤中默認值 ) 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="兩次密碼輸入不一致") #驗證2次輸入的密碼是否一致? ], 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'), #生成email input標籤 render_kw={'class': 'form-control'} ) gender = core.RadioField( label='性別', choices=( #choice radio選項 (1, '男'), (2, '女'), ), coerce=int #講用戶提交過來的 '4' 強制轉成 int 4 ) city = core.SelectField( label='城市', choices=( ('bj', '北京'), ('sh', '上海'), ) ) hobby = core.SelectMultipleField( #select 下拉框多選框 label='愛好', choices=( (1, '籃球'), (2, '足球'), ), coerce=int ) favor = core.SelectMultipleField( label='喜愛', choices=( (1, '籃球'), (2, '足球'), ), widget=widgets.ListWidget(prefix_label=False), #生成Checkbox 多選框 option_widget=widgets.CheckboxInput(), coerce=int, default=[1, 2] ) def__init__(self, *args, **kwargs): #重寫form驗證類的__init__方法能夠實時同步數據中數據 super(RegisterForm, self).__init__(*args, **kwargs) self.favor.choices = ((1, '籃球'), (2, '足球'), (3, '羽毛球')) def validate_pwd_confirm(self, field): #wtforms驗證 鉤子函數""" 自定義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()
<!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>
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__方法;
class InputText(object): #插件def__str__(self):return'<input type="text" />' class InputPassword(object):def__str__(self):return'<input type="password" />' #-----------------------------------------------------------class StringField(object): #字段def__init__(self,wg): self.widget=wg def__str__(self): #調用插件的__str__return str(self.widget) class DateField(object):def__init__(self, wg): self.widget = wg def__str__(self):return str(self.widget) #--------------------------------------------------------------class LoginForm(object): #統一 靈活接口 (對象嵌套對象,多層封裝) name=StringField(wg=InputText()) #wg=InputText() 對象 StringField(wg=InputText())對象 pwd=DateField(wg=InputPassword()) l_obj=LoginForm() print(l_obj.name)print(l_obj.pwd)
B.數據校驗
後臺定義好正則
用戶發來數據
對數據進行校驗
3.3.源碼流程
生成HTML標籤並顯示
1.驗證類(LogibForm)生成
1.1.因爲 metaclass=FormMeta,因此LoginForm是由FormMeta建立的
'''class BaseForm(): pass class NewBase(BaseForm,metaclass=FormMeta,): passclass Form(NewBase): pass class LoginForm(Form): pass''' class Form(with_metaclass(FormMeta,BaseForm)):
1.2.執行FormMeta 的__init__方法,在LoginForm中添加2個靜態字段
class FormMeta(type):""" The metaclass for `Form` and any subclasses of `Form`. `FormMeta`'s responsibility is to create the `_unbound_fields` list, which is a list of `UnboundField` instances sorted by their order of instantiation. The list is created at the first instantiation of the form. If any fields are added/removed from the form, the list is cleared to be re-generated on the next instantiation. Any properties which begin with an underscore or are not `UnboundField` instances are ignored by the metaclass."""def__init__(cls, name, bases, attrs): type.__init__(cls, name, bases, attrs) #繼承type的功能 cls._unbound_fields = None #在LoginForm中添加1個靜態字段 cls._wtforms_meta = None #在LoginForm中添加1個靜態字段
1.3.開始解釋LoginForm中的 實例化字段對象name=simple.StringField()simple.PasswordField()
StringField/PasswordField開始實例化(提到實例化就應該想到:指定元類的__call__、本身/父類的__new__、__init__):
StringField/PasswordField是默認元類,本身沒有__new__和__init__方法;
但父類Field類中有__new__方法,因此執行父類的__new__(Field.__new__)返回UnboundField對象
def__new__(cls, *args, **kwargs):#執行__new__方法if'_form'in kwargs and'_name'in kwargs:return super(Field, cls).__new__(cls)else:#我x 沒想到 ! __new__既然返回了1個 UnboundField()而不是StringField/PasswordField對象;狸貓換了太子 ?return UnboundField(cls, *args, **kwargs)
因爲Field.__new__方法返回了 1個 UnboundField對象,來看 UnboundField的__init__方法
class UnboundField(object): _formfield = True creation_counter = 0 #靜態字段 設置計數器 def__init__(self, field_class, *args, **kwargs): #field_class=.StringField / PasswordField#獲取到field_class 的 參數封裝到 UnboundField對象中,而且設置 排序 'creation_counter': 2 UnboundField.creation_counter += 1 #每實例化1個 UnboundField對象 計數器+1 self.field_class = field_class self.args = args self.kwargs = kwargs #{'label': '用戶名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000038EF080>, <wtforms.validators.Length object at 0x00000000038EF0F0>], 'widget': <wtforms.widgets.core.TextInput object at 0x00000000038EF0B8>, 'render_kw': {'class': 'form-control'}} self.creation_counter = UnboundField.creation_counter#''' print(self.__dict__) { 'field_class': <class 'wtforms.fields.simple.PasswordField'>, 'args': (), 'kwargs': {'label': '密碼', 'validators': [<wtforms.validators.DataRequired object at 0x00000000038EF198>, <wtforms.validators.Length object at 0x00000000038EF1D0>, <wtforms.validators.Regexp object at 0x00000000038EF208>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x00000000038EF2B0>, 'render_kw': {'class': 'form-control'}}, 'creation_counter': 2 }'''
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__實質性決定了實例化出來的對象是神馬?
class Queen(object):def__new__(cls, *args, **kwargs): #類中__new__方法決定了類(),實例化出什麼對象;return Cat('狸貓','男','太子') def__init__(self,name): #因爲__nwe__方法返回了其餘對象,因此不會執行Queen的__init__方法print('ok') self.name=name Prince=Queen('王子')print(Prince.name)print(Prince.gender)print(Prince.identity)
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屬性
def__call__(cls, *args, **kwargs):if cls._unbound_fields is None: #在建立類時 已經設置LoginForm的_unbound_fields爲空 fields = []# 獲取LoginForm類中,中全部屬性的key:[ '_get_translations', '_unbound_fields', '_wtforms_met,'name', 'populate_obj', 'process', 'pwd', 'validate'..... ]for name in dir(cls):ifnot name.startswith('_'): #排除__下劃線的私有屬性 name. pwd unbound_field = getattr(cls, name) #cls =LoginForm類 #根據key 獲取unbound_field 對象if hasattr(unbound_field, '_formfield'): #檢查unbound_field 對象是否包含_formfield = True fields.append((name, unbound_field))# '''# fields = [# (name,name的unbound對象),# (pwd,pwd的unbound對象),# ]# '''#對fields 按照定義順序 進行排序 fields.sort(key=lambda x: (x[1].creation_counter, x[0])) #根據unbound對象的creation_counter進行字段排序 cls._unbound_fields = fields if cls._wtforms_meta is None: bases = [] #bases = [DefaultMeta],# 按照繼承順序:獲取當前類(FormLogin)全部父類for mro_class in cls.__mro__:if'Meta'in mro_class.__dict__: #去每一個父類(mro_class)獲取 Meta = DefaultMeta bases.append(mro_class.Meta) #bases = [DefaultMeta], ''' class Meta(DefaultMeta): pass ''' cls._wtforms_meta = type('Meta', tuple(bases), {}) #cls._wtforms_meta=Meta(DefaultMeta)類: return type.__call__(cls, *args, **kwargs)
執行完了指定元類 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)類,}"""
啓發:
#sort排序v1=[ (11,'Martin11',18), (121,'Martin121',19), (311,'Martin311',25), (311, 'Martin311', 26) #按元素1排序,若是元素1相同按照 元素3排序] v1.sort(key=lambda x:(x[0],x[2])) #列表的sort方法,根據 列表中的元組元素 進行排序print(v1)''' [(11, 'Martin11', 18), (121, 'Martin121', 19), (311, 'Martin311', 25), (311, 'Martin311', 26)]'''
class F1(object):passclass F2(object):passclass F3(F1):pass class F4(F2,F3):pass print(F4.__mro__) #打印F4 的繼承關係 '''( <class '__main__.F4'>, <class '__main__.F2'>, <class '__main__.F3'>, <class '__main__.F1'>, <class 'object'>) '''
2.2.執行LoginForm的__new__方法
沒有__new__方法 pass
2.3.執行LoginForm的__init__方法實例化form對象
def__init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs): # 實例化LoginForm中封裝的 Meta類進行實例化,之後用於生成CSRF Tocken 標籤 meta_obj = self._wtforms_meta()#meta是 form = LoginForm(meta={'csrf':'true'})傳過來的參數,封裝到meta_obj中if meta isnot None and isinstance(meta, dict): meta_obj.update_values(meta) #執行父類的構造方法,參數# self._unbound_fields''' '_unbound_fields'=[ (name, UnboundField對象(1,simple.StringField,參數)), (pwd, UnboundField對象(2,simple.PasswordField,參數)), ],'''# meta_ob=Meta(DefaultMeta)對象 super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)#給 form對象 中的_fields字段賦值以下;''' _fields: { name: StringField對象(), pwd: PasswordField對象(), } name: StringField對象(widget=widgets.TextInput()), pwd: PasswordField對象(widget=widgets.PasswordInput())'''#循環form對象 中的_fields字段(字典),給form對象賦值 form.name/form.pwdfor name, field in iteritems(self._fields): setattr(self, name, field)''' _fields: { name: StringField對象(), pwd: PasswordField對象(), } name: StringField對象(widget=widgets.TextInput()), pwd: PasswordField對象(widget=widgets.PasswordInput()) ''' self.process(formdata, obj, data=data, **kwargs)
執行Form父類BaseForm.__init__方法,把UnboundField對象轉換成StringField對象,並賦值到form對象的_fields:{}字典中;
class BaseForm(object):def__init__(self, fields, prefix='', meta=DefaultMeta()):''' 參數 fields=[ (name, UnboundField對象(1,simple.StringField,參數)), (pwd, UnboundField對象(2,simple.PasswordField,參數)), ], meta=Meta(DefaultMeta)對象''' if prefix and prefix[-1] notin'-_;:/.': prefix += '-' self.meta = meta self._prefix = prefix self._errors = None self._fields = OrderedDict() if hasattr(fields, 'items'): fields = fields.items() translations = self._get_translations() extra_fields = []#------------------------------------------------------if meta.csrf: #生成 CSRF tocken隱藏標籤 self._csrf = meta.build_csrf(self) extra_fields.extend(self._csrf.setup_form(self)) for name, unbound_field in itertools.chain(fields, extra_fields):##(name, UnboundField對象(1,simple.StringField,參數))#(pwd, UnboundField對象(2,simple.PasswordField,參數)) options = dict(name=name, prefix=prefix, translations=translations)#(name, UnboundField對象(1,simple.StringField,參數)) #真正實例化 simple.StringField(參數) field = meta.bind_field(self, unbound_field, options)#UnboundField對象轉換成StringField對象 self._fields[name] = field
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__()
def__str__(self):return self() #執行LoginForm的__call__方法
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 isnot 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'notin kwargs: kwargs['value'] = field._value()if'required'notin kwargs and'required'in getattr(field, 'flags', []): kwargs['required'] = Truereturn 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功能
from flask import sessionimport jsonapp=Flask(__name__,template_folder='templates',static_path='/static/',static_url_path='/static/')app.debug=Trueapp.secret_key='sjehfjeefrjewth43u'#設置session加密app.config['JSON_AS_ASCII']=False #指定json編碼格式 若是爲False 就不使用ascii編碼,app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8"#指定瀏覽器渲染的文件類型,和解碼格式;@app.route('/login/',methods=['GET','POST'])def login(): msg = ''if request.method=='POST': name=request.values.get('user') pwd=request.values.get('pwd')if name =='zhanggen'and pwd=='123.com': session['user']=name #設置session的key valuereturn redirect('/index/')else: msg='用戶名或者密碼錯誤'return render_template('login.html',msg=msg) @app.route('/index/',methods=['GET','POST'])def index(): user_list = ['張根', 'egon', 'eric'] user=session.get('user') #獲取sessionif user: user=['alex','egon','eric']return jsonify(user_list)else:return redirect('/login/') if__name__ == '__main__': app.run()
2.第三方session組件(Session)
安裝 pip install flask-session
from flask import session, Flask,request,make_response,render_template,redirect,jsonify,Response from flask.ext.session import Session #引入第三方sessionimport jsonapp=Flask(__name__,template_folder='templates',static_path='/static/',static_url_path='/static/')app.debug=Trueapp.secret_key='sjehfjeefrjewth43u'#設置session加密app.config['JSON_AS_ASCII']=False #指定json編碼格式 若是爲False 就不使用ascii編碼,app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8"#指定瀏覽器渲染的文件類型,和解碼格式;app.config['SESSION_TYPE']='redis' from redis import Redis #引入鏈接 redis模塊app.config['SESSION_REDIS']=Redis(host='192.168.0.94',port=6379) #鏈接redisSession(app) @app.route('/login/',methods=['GET','POST'])def login(): msg = ''if request.method=='POST': name=request.values.get('user') pwd=request.values.get('pwd')if name =='zhanggen'and pwd=='123.com': session['user']=name #設置session的key valuereturn redirect('/index/')else: msg='用戶名或者密碼錯誤'return render_template('login.html',msg=msg) @app.route('/index/',methods=['GET','POST'])def index(): user_list = ['張根', 'egon', 'eric'] user=session.get('user') #獲取sessionif user: user=['alex','egon','eric']return jsonify(user_list)else:return redirect('/login/') if__name__ == '__main__': app.run()
不只能夠把session存放到redis還可放到文件、內存、memcache...
def _get_interface(self, app): config = app.config.copy() config.setdefault('SESSION_TYPE', 'null') config.setdefault('SESSION_PERMANENT', True) config.setdefault('SESSION_USE_SIGNER', False) config.setdefault('SESSION_KEY_PREFIX', 'session:') config.setdefault('SESSION_REDIS', None) config.setdefault('SESSION_MEMCACHED', None) config.setdefault('SESSION_FILE_DIR', os.path.join(os.getcwd(), 'flask_session')) config.setdefault('SESSION_FILE_THRESHOLD', 500) config.setdefault('SESSION_FILE_MODE', 384) config.setdefault('SESSION_MONGODB', None) config.setdefault('SESSION_MONGODB_DB', 'flask_session') config.setdefault('SESSION_MONGODB_COLLECT', 'sessions') config.setdefault('SESSION_SQLALCHEMY', None) config.setdefault('SESSION_SQLALCHEMY_TABLE', 'sessions') if config['SESSION_TYPE'] == 'redis': session_interface = RedisSessionInterface( config['SESSION_REDIS'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])elif config['SESSION_TYPE'] == 'memcached': session_interface = MemcachedSessionInterface( config['SESSION_MEMCACHED'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])elif config['SESSION_TYPE'] == 'filesystem': session_interface = FileSystemSessionInterface( config['SESSION_FILE_DIR'], config['SESSION_FILE_THRESHOLD'], config['SESSION_FILE_MODE'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])elif config['SESSION_TYPE'] == 'mongodb': session_interface = MongoDBSessionInterface( config['SESSION_MONGODB'], config['SESSION_MONGODB_DB'], config['SESSION_MONGODB_COLLECT'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])elif config['SESSION_TYPE'] == 'sqlalchemy': session_interface = SqlAlchemySessionInterface( app, config['SESSION_SQLALCHEMY'], config['SESSION_SQLALCHEMY_TABLE'], config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])else: session_interface = NullSessionInterface() return session_interface
3.自定義session組件
#!/usr/bin/env python# -*- coding:utf-8 -*-import uuidimport jsonfrom flask.sessions import SessionInterfacefrom flask.sessions import SessionMixinfrom itsdangerous import Signer, BadSignature, want_bytes class MySession(dict, SessionMixin):def__init__(self, initial=None, sid=None): self.sid = sid self.initial = initial super(MySession, self).__init__(initial or ()) def__setitem__(self, key, value): super(MySession, self).__setitem__(key, value) def__getitem__(self, item):return super(MySession, self).__getitem__(item) def__delitem__(self, key): super(MySession, self).__delitem__(key) class MySessionInterface(SessionInterface): session_class = MySession container = {} def__init__(self):import redis self.redis = redis.Redis() def _generate_sid(self):return str(uuid.uuid4()) def _get_signer(self, app):ifnot app.secret_key:return Nonereturn Signer(app.secret_key, salt='flask-session', key_derivation='hmac') def open_session(self, app, request):""" 程序剛啓動時執行,須要返回一個session對象""" sid = request.cookies.get(app.session_cookie_name)ifnot sid: sid = self._generate_sid()return self.session_class(sid=sid) signer = self._get_signer(app)try: sid_as_bytes = signer.unsign(sid) sid = sid_as_bytes.decode()except BadSignature: sid = self._generate_sid()return self.session_class(sid=sid) # session保存在redis中# val = self.redis.get(sid)# session保存在內存中 val = self.container.get(sid) if val isnot None:try: data = json.loads(val)return self.session_class(data, sid=sid)except:return self.session_class(sid=sid)return self.session_class(sid=sid) def save_session(self, app, session, response):""" 程序結束前執行,能夠保存session中全部的值 如: 保存到resit 寫入到用戶cookie""" domain = self.get_cookie_domain(app) path = self.get_cookie_path(app) httponly = self.get_cookie_httponly(app) secure = self.get_cookie_secure(app) expires = self.get_expiration_time(app, session) val = json.dumps(dict(session)) # session保存在redis中# self.redis.setex(name=session.sid, value=val, time=app.permanent_session_lifetime)# session保存在內存中 self.container.setdefault(session.sid, val) session_id = self._get_signer(app).sign(want_bytes(session.sid)) response.set_cookie(app.session_cookie_name, session_id, expires=expires, httponly=httponly, domain=domain, path=path, secure=secure)
from flask import Flaskfrom flask import sessionfrom my_session import MySessionInterface app = Flask(__name__) app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'app.session_interface = MySessionInterface() @app.route('/login/', methods=['GET', "POST"])def login():print(session) session['user1'] = 'alex' session['user2'] = 'alex'del session['user2'] return"內容" if__name__ == '__main__': app.run()
7、藍圖
使用Flask自帶Blueprintmuk模塊,幫助咱們作代碼目錄結構的歸類
import luffy #導入luffy包就會執行luffy包中__init__.py文件luffy.app.run()
from flask import Flask app=Flask(__name__,template_folder='templates',static_path='/static/',static_url_path='/static/') app.debug=True from .views import loginfrom .views import index #把文件中藍圖對象註冊到app裏app.register_blueprint(login.login,url_prefix='/login') #訪問login藍圖必須以url_prefix開頭app.register_blueprint(index.index,url_prefix='/index') if__name__ == '__main__': app.run()
from flask import Blueprint #導入藍圖login=Blueprint('login',__name__) #在本模塊實例化1個藍圖@login.route('/login/',methods=['GET','POST'])def login1():return'登陸頁面'
from flask import Blueprintindex=Blueprint('index',__name__)@index.route('/index/',methods=['GET','POST'])def index1():return'首頁'
8、message (閃現)
message是一個基於Session實現的用於保存數據的集合,其特色是:一次性。
特色:和labada匿名函數同樣不長期佔用內存
from flask import Flask,request,flash,get_flashed_messages app = Flask(__name__)app.secret_key = 'some_secret' @app.route('/set/')def index2(): flash('Disposable') #在message中設置1個個值return'ok' #---------------------------------------------------------------------------------@app.route('/')def index1(): messages = get_flashed_messages() #獲取message中設置的值,只能獲取1次。(1次性)print(messages)return"Index1" if__name__ == "__main__": app.run()
9、中間件
flask也有中間件功能和Django相似,不一樣的是使用的是使用3個裝飾器來實現的;
1.@app.before_first_request :請求第1次到來執行1次,以後都不執行;
2.@app.before_request:請求到達視圖以前執行;(改函數不能有返回值,不然直接在當前返回)
3.@app.after_request:請求 通過視圖以後執行;(最下面的先執行)
#!/usr/bin/env python# -*- coding:utf-8 -*-from flask import Flask, Request, render_template app = Flask(__name__, template_folder='templates')app.debug = True @app.before_first_request #第1個請求到來執行def before_first_request1():print('before_first_request1') @app.before_request #中間件2def before_request1(): Request.nnn = 123print('before_request1') #不能有返回值,一旦有返回值在當前返回 @app.before_requestdef before_request2():print('before_request2') @app.errorhandler(404)def page_not_found(error):return'This page does not exist', 404 @app.route('/')def hello_world():return"Hello World" @app.after_request #中間件 執行視圖以後def after_request1(response):print('after_request1', response)return response @app.after_request #中間件 執行視圖以後 先執行 after_request2def after_request2(response):print('after_request2', response)return response if__name__ == '__main__': app.run()
10、Flask相關組件
二、flask-script組件
flask-script組件:用於經過腳本的形式,啓動 flask;(實現相似Django的python manager.py runserver 0.0.0.0:8001)
pip install flask-script #安裝
#!/usr/bin/env python# -*- coding:utf-8 -*- from sansa import create_appfrom flask_script import Manager #導入app = create_app() manager=Manager(app) #實例化Manager對象 if__name__ == '__main__': manager.run()
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 。
#!/usr/bin/env python# -*- coding:utf-8 -*- from sansa import create_app,dbfrom flask_script import Manager #導入from flask_migrate import Migrate,MigrateCommand app = create_app()manager=Manager(app) #實例化Manager對象migrate=Migrate(app,db) manager.add_command('db',MigrateCommand) #註冊命令 if__name__ == '__main__': manager.run()
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.
文檔: http://docs.jinkan.org/docs/flask/quickstart.html
銀角大王:http://www.cnblogs.com/wupeiqi/articles/7552008.html
銀角大王:http://www.cnblogs.com/wupeiqi/articles/5713330.html(執行原生SQL模塊 pymsql ,ORM框架 SQLAchemy)