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

上下文具体分析:

全局分析:请求一过来,把有关请求的的值放到一个特殊的地方。

常见问题:

未完:待续……

posted @ 2018-04-26 14:29  zz小公子  阅读(179)  评论(0编辑  收藏  举报