python3-开发进阶Flask的基础(4)
今日内容:
- 上下文管理: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
helper.py
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()
test.py
先思考几个问题:
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__