flask学习二
@(python之路)[flask学习二]
flask学习二
面向对象
三大特性
1、封装
- 对函数进行封装
- 对数据进行封装
2、继承
3、多态
def func(arg)
arg.send()
arg可以是任何类对象,这就是多态(鸭子模型)
常用方法__str__
init
初始化方法,一般类后边加对象自动执行
call
对象后边加括号执行
new
** setattr**
setitem
enter
exit
dict
doc
del
name
class Foo:
x = 11111111
def __init__(self,name):
self.name = name
def __getitem__(self, item):
print("获取值时执行")
print(self.__dict__[item])
def __setitem__(self, key, value):
print("赋值时执行")
self.__dict__[key] = value
def __delitem__(self, key):
print("del obj[key]时,我执行")
self.__dict__.pop(key)
def __delattr__(self, item):
print("删除属性时,我执行")
self.__dict__.pop(item)
def __setattr__(self, key, value):
print("修改或添加属性才会执行")
def __getattr__(self, item):
print("只有调用不存在的是才会执行")
f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1['age']
f1['name']='alex'
del f1.age1
f1.x = 2222
f1.cccc
上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
class Open:
def __init__(self,name):
self.name=name
def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
# return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊')
with Open('a.txt') as f:
print('=====>执行代码块')
# print(f,f.name)
俩个对象时可以加减乘除的不过需要改他内部的__方法,例如两个对象相加就要改__add__方法;
对象之间可以加减乘除例如:相加用__add__(self,other)
metaclass(元类)
类是默认由type创建,meaclass指定谁来创建
# 创建类:方法一
class Foo(object):
pass
print(Foo)
# 创建类:方法二
Bar= type("Foo",(object),{})
print(Bar)
class MyType(type):
def __init__(self,*args,**kwargs)
super(MyType,self).__init__(*args,**kwargs)
class Foo(mtaclass=MyType): # 默认每个的metaclass=type
pass
Flask基础
蓝图
作用:规划目录结构,请求扩展等功能,一部分url的 前缀功能;
蓝图的基本标准的目录结构
pro_flask
|__pro_flask
|__views
|__account.py
|__user.py
__init__.py
manage.py
注意:
这里的pro_flask和其子目录的pro_flask要一样;
# manage.py
from pro_flask import app
if __name__ == '__main__':
app.run()
# 这里我们需要调用__init__下的app
# __init__.py
from flask import Flask
app = Flask(__name__)
from .views import account
from .views import user
# 这里把views下的自定义的路由匹配,相当于挂在到当下
app.register_blueprint(account.ac)
app.register_blueprint(user.us)
# account.py
from flask import Blueprint
# 我们通过蓝图,定义各自的app;
# rul_prefix我们再次访问的时候就需要奖赏/xx/login才可以
ac = Blueprint('ac',__name__,url_prefix="/xx")
@ac.route('/login')
def login():
return 'login'
@ac.route('/logout')
def logout():
return 'logout'
# user.py
from flask import Blueprint
us = Blueprint('us',__name__,url_prefix='/xx')
# 这里的before_request可以定义在最顶层,也可以定在每个app下,就是user.py下。
# @us.before_request
# def check_login():
# print('.....')
@us.route('/index')
def index():
return 'index'
闪现
什么是闪现
我们在一个视图中创建一个数据,当出现错误调到另一个视图;前提是设置一次,那一次多了没有;
方式一
@app.route("/f1")
def f1():
v = "操作失败"
return redirect("/f2?msg=%s"%v)
@app.route("/f2")
def f2():
# 获取数据
v = request.args.get("msg")
return v
弊端:提供的错误信息有可能别人的有机可乘;传值的时候涉及安全问题
方式二
@app.route("/f1")
def f1():
session["v"] = "操作失败"
return redirect("/f2")
@app.route("/f2")
def f2():
# 获取数据
v = session.pop["v"] # 这个错误值经常用可以,但是用一次用pop
return v
方式三
@app.route("/f1")
def f1():
flash("操作失败1",category="x1") # 这里相当个操作添加一个k,报错信息是v
flash("操作失败2",category="x2")
return redirect("/f2”)
@app.route("/f2")
def f2():
# 获取数据
data = get_flashed_messages(category_filter=["x1",]) # 根据需要获取值列表可以,有多个报错信息的值;
return v
方式三的源码也是根据session去弄的,有兴趣可以看一看;他不会传,因为这里是用的session去做。
中间件
在请求刚进来的时候,他会执行Flask的__call__方法。
那么需求,需要在请求来之前做一些处理?
from flask import Flask
app = Flask(__name__)
@app.route("/index")
def index():
return "Hello World"
class Middleware(object):
def __init__(self,old_wsgi_app):
self.old_wsgi_app = old_wsgi_app
def __call__(self, *args, **kwargs):
# 这里还没有request,所以不能做一些 请求相关的操作;
# 在源码中执行完以下这一步才会有request
'''
ctx = self.request_context(environ)
ctx.push()
error = None
'''
response = self.old_wsgi_app
return response
if __name__ == '__main__':
app.wsgi_app = Middleware(app.wsgi_app)
app.run()
当请求进来的时候,首先Flask的__call__方法下的wsgi_app。我们通过继承原有的方法让他执行我们自己的方法,这种方式在请求来和请求之后做一些处理;
扩展
Flask使用redis和threading.local
Flask实现redis最为缓存
在真实的环境中,我们用redis存放我们的缓存等一系列东西,我们需要安装两个模块,这样我们不需要改代码,只需要加几行代码就好了。他会把内存中的东西放到redis中;
flask-session
安装:pip3 install flask-session
安装:pip install redis
from flask import Flask,session
app = Flask(__name__)
app.secret_key = "fjlajflasjfl"
# 我们 只需要添加这几条配置,我们Flask的中session存的东西都会放到session中;
from flask.ext.session import Session
from redis import Redis
# Flask中应用的redis配置;所有代码都不用改
app.config["SESSION_TYPE"] = "redis"
app.config["SESSION_REDIS"] = Redis(host="redis的IP地址",port="端口号",password="密码")
# 接下来我们看一下源码都做了什么
'''
def __init__(self, app=None):
self.app = app
# 如果部位空,就执行init_app
if app is not None:
# 我们看一下,init_app
'''
# 他会执行_get_interface(app)
'''
def _get_interface(self,opp)
…… 这里时配置信息
# 因为我们在代码中添加了配置文件,所以他会走我们写的那个配置文件,
if config['SESSION_TYPE'] == 'redis':
session_interface = RedisSessionInterface(
config['SESSION_REDIS'], config['SESSION_KEY_PREFIX'],
config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
…… 其他缓存服务的配置
'''
app.session_interface = self._get_interface(app)
'''
self.init_app(app)
'''
Session(app)
# Session(app)的本质: app.session_interface = RedisSessionInterface()
# 我们查看源码可以得到,他实际在请求到来的时候时执行了,RedisSessionInterface.open_session(self,request)
# 我们找到RedisSessionInterface.open_session(self,request)的open_session();
'''
def open_session(self, app, request):
# 这里是获取用户cookie中原来设置的随机字符串:qetfgsdgsdgewtxcvee
sid = request.cookies.get(app.session_cookie_name)
if not sid:
'''
这里的_generate_sid(),做了什么他返回了一个uuid
def _generate_sid(self):
return str(uuid4())
'''
sid = self._generate_sid()
# 这里相当于创建了一个特殊的字典放到内存中,
return self.session_class(sid=sid, permanent=self.permanent)
'''
@app.route("/login")
def login():
session["user_info"] = "xxxxxx"
return "login" # 在走得时候要执行save_session
# 当执行完视图之后,我们看一下session的操作
'''
def save_session(self, app, session, response):
domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app)
if not session:
if session.modified:
self.redis.delete(self.key_prefix + session.sid)
response.delete_cookie(app.session_cookie_name,
domain=domain, path=path)
return
httponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app)
expires = self.get_expiration_time(app, session)
# 把特殊的字典转换为字典;进行序列化,变成字符串;这就是我们的val
val = self.serializer.dumps(dict(session))
# 这里的time表示超时时间
self.redis.setex(name=self.key_prefix + session.sid, value=val,
time=total_seconds(app.permanent_session_lifetime))
if self.use_signer:
session_id = self._get_signer(app).sign(want_bytes(session.sid))
else:
# session.sic就是session的ID,又把随机字符串写到cookie里了;
session_id = session.sid
response.set_cookie(app.session_cookie_name, session_id,
expires=expires, httponly=httponly,
domain=domain, path=path, secure=secure)
'''
@app.route("/index")
def index():
print(session["user_info"])
return "index"
if __name__ == '__main__':
# 接下来请求到来,我们就可以顺着源码找了
#
app.run()
threading.local
为每一个线程开辟一块独立的空间,让他存放数据。
import threading
import time
# 导入local模块
obj = threading.local()
def task(arg):
# 传入数据
obj.value = arg
time.sleep(1)
print(obj.value)
for i in range(10):
t = threading.Thread(target=task,args=(i,))
t.start()
原理:它的内部就是一个字典,字典的k就是线程的唯一的表示;我们可以模仿去实现。
例如:
import time
import threading
from threading import get_ident
class Local(object):
def __init__(self):
self.storage = {}
# 赋值时候的操作
def __setitem__(self, key, value):
ident = get_ident()
if ident in self.storage:
self.storage[ident][key] = value
else:
self.storage[ident] = {key:value}
# 获取值时候的操作
def __getitem__(self, key):
ident = get_ident()
return self.storage[ident][key]
obj = Local()
def task(arg):
obj["value"] = arg
time.sleep(1)
print(obj["value"])
for i in range(10):
t = threading.Thread(target=task,args=(i,))
t.start()
在Flask中他用的是我自定的这种方式,而不是用的threading。
我们这样处理,那么协成也是样的。
源码位置globals.py---->_request_ctx_stack = LocalStack()----->
初始化方法中,self._local = Local()
try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident
# 如果有协成使用协成,分工更细了。
上下文管理
流程分析
请求进来的时候,执行__call__
方法。
# environ 这个参数其实是werkzeug 去帮我们做的,请求相关的东西
def __call_(self,environ,start_response):
return self.wsgi_app(environ,start_sesponse )
# 继续往下执行
def wsgi_app(self, environ, start_response):
# ctx = self.request_context(environ)
# ctx = RequestContext对象(app对象,请求相关所有数据)
# 路由匹配
# 这个RequestContext存放了好多东西,包括请求相关的东西。我们重点看self.match_request()
# 这里就是路由匹配,
# 路由匹配:
# 获取用户请求的URL
# 根据url_map中的对应关系进行比较
ctx = self.request_context(environ)
# ctx.push()
# 这里最终_rquest_ctx_stack.push()
# 他就是封装了所有的session和请求相关的示例对象_rquest_ctx_stack
# def push(se lf,obj) # 这里的obj = (request/session)
# rv = getattr(self._local,"stack",None) # 这里边self._local才是真正存放数据的 地方。
'''
def push(self, obj):
#这里的obj = (request/session)
"""Pushes a new item to the stack"""
# 因为此处我们要看一下数据是怎么存的所以就要看一下
# self._local里边是怎么做的。这里不在展示。
# 这里就会创建一个唯一标识,写了一个字典把对象存进来了
# __storage__= {"唯一标识":[stack:{obj(xxx,sss)}]}
# 这里真正存储数据的滴地方是Local()
# def __init__(self):
# self._local = Local()
rv = getattr(self._local, 'stack', None)
if rv is None:
self._local.stack = rv = []
# 把请求相关的东西放到一个列表里
rv.append(obj)
return rv
'''
ctx.push()
error = None
try:
try:
# 1、before_request
# 2、视图函数
# 3、after_request
'''self.full_dispatch_request()
def full_dispatch_request(self):
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
# 执行所有的 before_request
# 这里是执行所有的蓝图相关的东西preprocess_request,检查数据是否合法。如果合法在继续;
rv = self.preprocess_request()
# before_request全部通过
if rv is None:
# dispatch_request 找到视图函数在在执行
# return self.view_functions[rule.endpoint](**req.view_args)
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(e)
return self.finalize_request(rv)
'''
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except:
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
# 请求终止
# auto_pop---> self.pop ---> rv = _request_ctx_stack.pop() ——->
# _request_ctx_stack = LocalStack() ---> def pop(self) --->
# 找到stack然后就pop掉
ctx.auto_pop(error)
示例以及流程
from flask import Flask,session,request,current_app,g
app = Flask(__name__)
@app.route('/index')
def index():
"""
sdf
:return:
"""
"""
1. session.__setitem__ -> LocalProxy.__setitem__
1.1 v = self._get_current_object()
v = _lookup_req_object('session')
v = ctx.session
1.2 v[key] = value
ctx.session['x1'] = 123
"""
session['x1'] = 123
"""
1. request.__getattr__ -> LocalProxy.__getattr__
getattr(self._get_current_object(), name)
1.1 v = self._get_current_object()
v = _lookup_req_object('request')
v = ctx.request
1.2 getattr(v,"args") -> v.args
ctx.request.args
"""
request.args
current_app
return 'Hello World'
if __name__ == '__main__':
app.run()
偏函数
def func(a1,a2,a3):
return a1+a2+a3
import functools
f1 = functools.partial(func,68)
v1 = f1(2,3)
print(v1)
######### 打印 ############
73
# 这里的就是将68当作第一个参数传入func函数,func第一个参数有了固定值,所以我们得到了f1,此时f1的第一个参数为68,剩余的啷个参数需要自己传入。
# 这里的partial对函数进行分装,自动传入;
# f1 = functools.partial(func,68,20)
示例:这里根据Flask的源码举例
import functools
class RequestContext(object):
def __init__(self):
self.request = "ssssss"
self.session = "ilasfjosicmnlvajo"
obj = RequestContext()
def get_data(name):
return getattr(obj,name)
request_proxy = functools.partial(get_data,"request")
session_proxy = functools.partial(get_data,"session")
request = request_proxy()
session = session_proxy()
print(request)
print(session)
补充:
class Foo(object):
def __setattr__(self, key, value):
print(key,value)
def __getattr__(self, item):
print(item)
def __setitem__(self, key, value):
# obj["xx"] = 123
pass
def __getitem__(self, item):
# obj["xx"]
print(item)
obj = Foo()
obj.x = 123 # 这里的赋值x = key 123=value;他会执行__setattr__(self, key, value) 方法
obj.x # 这里会执行__getattr__(self,item)方法
######## 打印结果 ###########
x 123
Flask上下文管理之local对象
from threading import get_ident
class Local(object):
def __init__(self):
# 这里需要注意以下,如果我们直接在这里定义__storage__会报错,因为他会先
# 执行__setattr__方法
object.__setattr__(self,"__storage__",{})
def __setattr__(self, key, value):
ident = get_ident()
try:
self.__storage__[ident][key] = value
except KeyError as e:
self.__storage__[ident] = {key:value}
def __getattr__(self, item):
print("getattr",item)
obj = Local()
obj.stack = []
obj.stack
注:
我们这里是根据Flask的源码去写的;
我们根据 源码写
from threading import get_ident
class Local(object):
'''
存数据
'''
def __init__(self):
object.__setattr__(self,"__storage__",{})
def __setattr__(self, key, value):
ident = get_ident()
try:
self.__storage__[ident][key] = value
except KeyError as e:
self.__storage__[ident] = {key:value}
def __getattr__(self, item):
ident = get_ident()
try:
return self.__storage__[ident][item]
except Exception as e:
return None
class LocalStack(object):
def __init__(self):
self._local = Local()
def push(self,data):
stack = self._local.stack
if not stack:
self._local.stack = []
self._local.stack.append(data)
stack1 = LocalStack()
stack2 = LocalStack()
stack1.push("web1")
stack2.push("web2")
"""
local1 = {
"sdfsdfsdf": { stack: ['web1',] }
}
local2 = {
"sdfsdfsdf": { stack: ['web2',] }
}
"""
Flask上下文管理之LockStark对象
class LocalStack(object):
def __init__(self):
self._local = Local()
def push(self,data):
stack = self._local.stack
if not stack:
self._local.stack = []
self._local.stack.append(data)
stack1 = LocalStack()
stack2 = LocalStack()
stack1.push('x1')
stack2.push('xxxx')
""" # 当我们传值进去的时候,他会以字典的形式展示出来
# 有几个stark对象就有几个stack
local1 = {
"sdfsdfsdf": { stack: ['x1',] }
}
local2 = {
"sdfsdfsdf": { stack: ['xxxxxx',] }
}
"""
local和LocalStack对象联合使用
class RequestContext(object):
def __init__(self):
self.request = '666'
self.session = '999'
ctx = RequestContext()
stack1.push(ctx)
"""
local1 = {
'adfasdfasdf': { stack: [ctx(request,session) ] }
}
local2 = {
}
"""
stack2.push('app')
"""
local1 = {
'adfasdfasdf': { stack: [ctx(request,session) ] }
}
local2 = {
'adfasdfasdf': { stack: ["app" ] }
}
"""
源码真实流程:
在flask启动的时候,他会执行flask下的globals下的方法,然后在执行__call__
方法。
这里需要注意的是:
#context locals
_request_ctx_stack = LocalStack(
‘40044’:{
stack:[ctx(request,session),]
})
_app_ctx_stack = LocalStack(
'40044':{
stack:[app_ctx(app,g),]
}
)
当我们启动之后,他会先执行__call__
方法,当请求来的时候他会把请求对象ctx = RequestContext对象(app对象,请求相关所有数据)先push到一个位置就是local中(ctx.push)。这里会涉及到两个上下文,一个是_request_ctx_stack
这里主要存放request,session请求上下文相关信息;另一个_app_ctx_stack
这里主要存放应用上下文相关信息;
LocalProxy对象
import functools
"""
_request_ctx_stack =- _local = {
'4041': {
stack: [ctx(reuqest,session),]
},
}
_app_ctx_stack = _local = {
'4041': {
stack: [app_ctx(app,g),]
},
}
"""
def open_file(name):
obj = _request_ctx_stack._local[4041][stack][0]
return getattr(obj,name)
class LocalProxy(object):
def __init__(self,func):
self.func = func
def __add__(self, other):
pass
def get_file_obj(self):
return self.func() # open_file('session')、open_file(request)
def __init__(self):
pass
def __setitem__(self, key, value):
a = self.get_file_obj()
def __getitem__(self, item):
pass
session = LocalProxy(functools.partial(open_file,'session'))
request = LocalProxy(functools.partial(open_file,'request')) # functools.partical(x,y)这里把y当成第一个参数传到x函数中。
def index():
session['x'] = 123
request['xx'] = 999
上下文具体分析:
全局分析:请求一过来,把有关请求的的值放到一个特殊的地方。
常见问题:
未完:待续……