今日內容:
- 上下文管理:LocalProxy對象
- 上下文管理: 請求上下文: request/session app上下文:app/g
- 第三方組件:wtforms 1、使用 2、原理
一、LocalProxy
首先我們一看一段自己寫的代碼:
#test.py DATA={ 'request':{ 'method':'GET', 'form':{} }, 'session':{ 'user':'duoduo', 'age':'20' } } class LocalProxy(object): # def __init__(self,key): self.key=key def get_dict(self): return DATA[self.key] def __str__(self): return 'duoduo' def __getattr__(self, item): data_dict=self.get_dict() return data_dict[item] def __getitem__(self, item): data_dict = self.get_dict() return data_dict[item] def __add__(self, other): return other+1 request=LocalProxy('request') session=LocalProxy('session')
LocalProxy的類就相當於一個代理
下面我們來看看源碼:先來個最簡單flask
from flask import Flask duo=Flask(__name__) @duo.route('/index/') def index(): return "hello world" if __name__ == '__main__': duo.run()
我們把flask分為二個階段,
第一階段:
請求到來 先執行app.__call__ --->wsgi_app--->實例化RequestContext對象賦值給ctx
在ctx中 ctx.request=Request(environ),ctx.session=None
(具體描述:將request和Session 相關數據封裝到 ctx=RequestContext對象中)
ctx.push()------->_request_ctx_stack.push(self) 這個self就是包含了request,session的ctx
_request_ctx_stack=LocalStack() 單例,全局變量,只有一個
(具體描述:再通過LocalStack將ctx添加到Local中)
存儲的格式: __storage__={唯一字段:{’stack:[ctx(request,session)]‘}}
第二階段:視圖函數中獲取request或session
方式一:直接找LocalStack獲取
from flask.globals import _request_ctx_stack #先這句寫在視圖函數中 print(_request_ctx_stack.top.request.method)
方式二:通過代理LocalProxy獲取
from flask import Flask,request print(request.method)
他是怎么實現的,導入的request是在哪放着
我們點開導入的request,看看是什么:
我們猜想request.method有值,那么LocalProxy有method 或者是__getattr__方法,點開源碼看看里邊到底有沒有
沒有method,那么就是另一這種可能,__getattr__
調用偏函數
二、上下文管理: 請求上下文: request/session app上下文:app/g
1、程序啟動:
我們創建兩個Local:還有兩個LocalStack,
_request_ctx_stack
_app_ctx_stack
2、請求到來
對數據進行封裝
ctx=RequestContext(request,session)
app_ctx=AppContext(app,g)
保存數據
將包含了(app,g)數據的app_ctx對象,利用_app_ctx_stack=LocalStack()將app_ctx添加到Local中
storage={'唯一標識:{stack:[app_ctx(app,g),]}'}
將包含了request,session數據的ctx對象,利用_request_ctx_stack=LocakStack(),將ctx添加到Local中
storage={'唯一標識:{stack:[ctx(request,session),]}'}
3、視圖函數處理:
直接導入,然后相應的獲取值,一個是請求上下文取值,一個是app上下文取值
4、結束
_app_ctx_stack.pop
_request_ctx_stack.pop
這下值就去取走了
我們來看源碼:
from flask import globals #點globals
他們都在最開始就創建了local對象
請求進來,執行__call__---》wsgi_app--->生成ctx對象----》ctx.push,再看這里的源碼
再來看看 AppContext
才會去看看 app_ctx.push()
現在我們提出一個問題:
1、flask中g的生命周期?
在請求來的時候g就創建了,當請求走的時候g的生命周期一結束,
2、g和session一樣嗎?
和session 是不一樣的,session的值還存在cookie中,還是可以取值
3、g和全局變量一樣?
不一樣,全局變量程序啟動就一直在內存中
4、上面的演示都是單線程的,多線程的時候會變嗎?
他們存值時,都是以線程的唯一標志存值,不影響的
三、flask 第三方組件 wtforms
作用:1、生成html標簽 2、form表單驗證
安裝:
pip3 install wtforms
實例:

import pymysql from DBUtils.PooledDB import PooledDB, SharedDBConnection import pymysql POOL = PooledDB( creator=pymysql, # 使用鏈接數據庫的模塊 maxconnections=6, # 連接池允許的最大連接數,0和None表示不限制連接數 mincached=2, # 初始化時,鏈接池中至少創建的空閑的鏈接,0表示不創建 maxcached=5, # 鏈接池中最多閑置的鏈接,0和None不限制 maxshared=3, # 鏈接池中最多共享的鏈接數量,0和None表示全部共享。PS: 無用,因為pymysql和MySQLdb等模塊的 threadsafety都為1,所有值無論設置為多少,_maxcached永遠為0,所以永遠是所有鏈接都共享。 blocking=True, # 連接池中如果沒有可用連接后,是否阻塞等待。True,等待;False,不等待然后報錯 maxusage=None, # 一個鏈接最多被重復使用的次數,None表示無限制 setsession=[], # 開始會話前執行的命令列表。如:["set datestyle to ...", "set time zone ..."] ping=0, # ping MySQL服務端,檢查是否服務可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always host='127.0.0.1', port=3306, user='root', password='123456', database='庫名', charset='utf8' ) def connect(type): conn = POOL.connection() cursor = conn.cursor(cursor=type) return conn,cursor def connect_close(conn,cursor): cursor.close() conn.close() def fetch_all(sql,args,type=pymysql.cursors.DictCursor): conn,cursor = connect(type) cursor.execute(sql, args) record_list = cursor.fetchall() connect_close(conn,cursor) return record_list def fetch_one(sql, args): conn, cursor = connect() cursor.execute(sql, args) result = cursor.fetchone() connect_close(conn, cursor) return result def insert(sql, args): conn, cursor = connect() row = cursor.execute(sql, args) conn.commit() connect_close(conn, cursor) return row

from flask import Flask,request,render_template,session,current_app,g,redirect from wtforms import Form from wtforms.fields import simple from wtforms.fields import html5 from wtforms.fields import core from wtforms import widgets from wtforms import validators app = Flask(__name__) class LoginForm(Form): name = simple.StringField( validators=[ validators.DataRequired(message='用戶名不能為空.'), # validators.Length(min=6, max=18, message='用戶名長度必須大於%(min)d且小於%(max)d') ], widget=widgets.TextInput(), render_kw={'placeholder':'請輸入用戶名'} ) pwd = simple.PasswordField( 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個特殊字符') ], render_kw={'placeholder':'請輸入密碼'} ) @app.route('/login',methods=['GET','POST']) def login(): if request.method == "GET": form = LoginForm() # print(form.name,type(form.name)) # form.name是StringField()對象, StringField().__str__ # print(form.pwd,type(form.pwd)) # form.pwd是PasswordField()對象,PasswordField().__str__ return render_template('login.html',form=form) form = LoginForm(formdata=request.form) if form.validate(): print(form.data) return redirect('https://www.luffycity.com/home') else: # print(form.errors) return render_template('login.html', form=form) class RegisterForm(Form): name = simple.StringField( label='用戶名', validators=[ validators.DataRequired() ], widget=widgets.TextInput(), render_kw={'class': 'form-control'}, default='alex' ) 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 # int("1") ) 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, ] ) @app.route('/register', methods=['GET', 'POST']) def register(): if request.method == 'GET': form = RegisterForm() return render_template('register.html', form=form) form = RegisterForm(formdata=request.form) if form.validate(): print(form.data) return redirect('https://www.baidu.com') return render_template('register.html', form=form) import helper class UserForm(Form): city = core.SelectField( label='城市', choices=(), coerce=int ) name = simple.StringField(label='姓名') def __init__(self,*args,**kwargs): super(UserForm,self).__init__(*args,**kwargs) self.city.choices=helper.fetch_all('select id,name from tb1',[],type=None) @app.route('/user') def user(): if request.method == "GET": #form = UserForm(data={'name':'duoduo','city':3}) form = UserForm() return render_template('user.html',form=form) if __name__ == '__main__': app.run()
先思考幾個問題:
1、form 對象為什么可以for循環?
變成一個可迭代對象,類中含有__iter__,並且返回一個迭代器
class Foo(object): # def __iter__(self): # return iter([11,22,33]) #值是11,22,33 def __iter__(self): yield 1 yield 2 yield 3 obj=Foo() for item in obj: print(item)
2、__new__方法返回值決定對象到底是什么
class Bar(object): def __init__(self,cls): self.cls=cls class Foo(object): def __new__(cls, *args, **kwargs): #return super(Foo,cls).__new__(cls,*args,**kwargs) <__main__.Foo object at 0x0000023741EEAC88> return Bar(cls) #<__main__.Bar object at 0x0000013FC1ABA4E0> obj=Foo() print(obj)
3、metaclass
- 創建類時,先執行type的__init__方法,當一個類實例化時,
- 執行type的__call__方法, 先執行類的__new__,創建對象再執行類的__init__,初始化
- __call__方法的返回值就是實例化的對象
#類創建的兩種方式 類是type創建的 #1、 # class Foo(object): # a1=123 # def func(self): # return 666 # Foo=type('Foo',(object,),{'a1':123,'func':lambda self:666}) #2、自定義type # # class MyType(type): # pass # # class Foo(object,metaclass=MyType): #指定當前類由誰創建 # a1=123 # def func(self): # return 666 # Foo=MyType('Foo',(object,),{'a1':123,'func':lambda self:666}) #3、metaclass 作用 #指定當前類由誰來創建 class MyType(type): def __init__(self,*args,**kwargs): super(MyType,self).__init__(*args,**kwargs) def __call__(cls, *args, **kwargs): obj=cls.__new__(cls) cls.__init__(obj,*args, **kwargs) return obj class Foo(object,metaclass=MyType): a1=123 def __init__(self): pass def __new__(cls, *args, **kwargs): return object.__new__(cls) def func(self): return 666 #Foo是類 #Foo是MyType的一個對象 obj=Foo()
4、wtforms的實現
from wtforms import Form #先點開Form
我們創建的類沒有metaclass但他繼承的父類,有指定metaclass,就按照父類的來創建類
看看FormMeta 中的__init__方法做了什么事?
實例化時先執行__new__
UnboundField類是做什么的?
做完自己的操作,再執行父類的__call__,然后type會調用自己__new__方法,__init__方法
下面Form 的序列化
源碼:
類的創建 type.__init__
對象的創建 type.__call__ 1、類.__new__ 2、類.__init__