python3-开发进阶Flask的基础(4)
今日内容:
- 上下文管理:LocalProxy对象
- 上下文管理: 请求上下文: request/session app上下文:app/g
- 第三方组件:wtforms 1、使用 2、原理
一、LocalProxy
首先我们一看一段自己写的代码:
#test.py
DATA={
'request':{
'method':'GET',
'form':{}
},
'session':{
'user':'duoduo',
'age':''
}
} 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='',
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__
python3-开发进阶Flask的基础(4)的更多相关文章
- python3开发进阶-Web框架的前奏
我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 1.自定义web框架 import socket ...
- python3-开发进阶Flask的基础(5)
内容概要: SQLAlchemy flsak-sqlalchemy flask-script flask-migrate Flask的目录结构 一.SQLAlchemy 1.概述 SQLAlchemy ...
- python3-开发进阶Flask的基础
一.概述 最大的特点:短小精悍.可拓展强的一个Web框架.注意点:上下文管理机制,依赖wsgi:werkzurg 模块 二.前奏学习werkzurg 先来回顾一个知识点:一个类加括号会执行__init ...
- python3开发进阶-Django框架的起飞加速一(ORM)
阅读目录 ORM介绍 Django中的ORM ORM中的Model ORM的操作 一.ORM介绍 1.ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一 ...
- python3开发进阶-Django框架起飞前的准备
阅读目录 安装 创建项目 运行 文件配置的说明 三个组件 一.安装(安装最新LTS版) Django官网下载页面 根据官方的图版本,我们下载1.11版本的,最好用! 有两种下载方式一种直接cmd里: ...
- python3-开发进阶Flask的基础(3)
上篇我们大概简单描述了一下上下文管理,这篇来具体来说说, 上下管理的request 上下管理的session 第三方组件:flask-session pymysql操作数据库 数据库连接池 一.前奏 ...
- python3-开发进阶Flask的基础(2)
知识回顾 1.django 和flask区别? 最大的不同就是django请求相关的数据,通过参数一个一个传递过去的,而flask就是先把放在某个地方,然后去取,这个东西叫上下文管理 2.什么是wsg ...
- python3开发进阶-Django框架的自带认证功能auth模块和User对象的基本操作
阅读目录 auth模块 User对象 认证进阶 一.auth模块 from django.contrib import auth django.contrib.auth中提供了许多方法,这里主要介绍其 ...
- python3开发进阶-Django框架的Form表单系统和基本操作
阅读目录 什么是Form组件 常用字段和插件 自定义校验的方式 补充进阶 一.什么是Form组件 我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标 ...
随机推荐
- Spring Tool Suite 配置和使用
Spring Tool Suite使用 1.下载地址: http://spring.io/tools 2.配置字符编码:UTF-8 默认的编码是ISO-8859-1的西欧文字编 1.windows-- ...
- Linux终端提示符PS1设置(颜色)
\d :代表日期,格式为weekday month date,例如:"Mon Aug 1"\H :完整的主机名称.例如:我的机器名称为:fc4.linux,则这个名称就是fc4.l ...
- filezilla显示隐藏文件
我们在习惯使用flashfxp等工具,但是随着主机商限制较多,这些老的FTP工具不怎么好用了,比如主机商会推荐使用到Filezilla等工具.但是比如息壤主机,我们在管理linux环境下htacess ...
- docker ubuntu容器更换阿里源(转)
问题:使用docker 利用下载的ubuntu镜像启动容器时,使用的源下载更新软件的速度较慢. 解决这个问题的方法是跟新ubuntu容器的源 示例:以ubuntu为基础镜像 启动一个名称为 test0 ...
- Linux 脚本内容指定用户执行
#!/bin/bash set -x ## 因为这些变量在下面要用,所以要写在最上面, ## 如果直接写在下面,则变量获取不到,并且下面的 $ 标识的都要用 引号引起来,否则这些参数接收不到 tarf ...
- 某线下赛AWD
拿别人比赛的来玩一下,或许这就是菜的力量吧. 0x01 任意文件读取: switch ($row['media_type']) { case 0: // 图片广告 ...... break; case ...
- systemd基础
systemd 起源 systemd这一名字源于Unix中的一个惯例:在Unix中常以“d”作为系统守护进程(英语:daemon,亦称后台进程)的后缀标识.除此以外,systemd亦是借代英文术语D体 ...
- promise应用于ajax
promise应用于ajax,可以在本页打开控制台,复制代码试验 var url = 'https://www.cnblogs.com/mvc/blog/news.aspx?blogApp=dkplu ...
- Eolinker——代码注入插入随机参数值
因为目前eolinker的API自动化测试不支持“构造参数”,所以用到随机数时,可使用代码注入的方式来实现 分步指南 示例:“重置密码”接口,每次运行重置的密码要求不重复 再此接口的“代码注入”区域写 ...
- 《自己动手写docker》之namespace部门实验
动手写一遍,印象不一样! package main import ( "log" "os" "os/exec" "syscall& ...