当前位置:   article > 正文

flask之路由与视图-蓝图-session原理-threading.local_flask 跨蓝图访问session

flask 跨蓝图访问session

1、路由和视图

# @app.route('/index')干了什么事?
1、先执行 app.route('/index'),返回值decorator
2、再执行:@decorator
3、把它装饰的函数当作参数传递到源码执行:decorator(func)
4、decorator源码如下:
def route(self, rule, **options):
    def decorator(f):
        endpoint = options.pop("endpoint", None)
        # rule='路由'
        # endpoint=别名,如果不传就是函数名,不能多个路由的重名
        # f=函数
        self.add_url_rule(rule, endpoint, f, **options)
        return f
    return decorator
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
路由设置的两种方式:
# 方式一(主流方式)
@app.route('/index')
def index():
    return 'index'

# 方式二
def index():
    return 'index'
app.add_url_rule('/index', None, index)  # (路由,别名,函数)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
参数
# app.route()的参数
rule:url
view_func:视图函数名称
defaults:给函数传递一个默认值
endpoint:反向解析起别名,即url_for('别名')
strict_slashes:这个写成False之后,路由对后面的/就不再严格要求
redirect_to:如果系统更新,可以把旧地址重定向到新地址,确保正常访问
subdomain:子域名访问
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
CBV
from flask import views, Flask
from functools import wraps

app = Flask(__name__)

def wrapper(func):
    @wraps(func)
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

class UserView(views.MethodView):
    # 先执行:self.dispatch_request,再决定执行哪个请求函数
    methods = ['GET', 'POST']  # 指定允许的请求方式
    decorators = [wrapper, ]  # 装饰器在请求方式执行之前执行,比如before_request装饰的功能
    def get(self, *args, **kwargs):
        return 'GET'
    
    def post(self, *args, **kwargs):
        return 'POST'
# 路由不能再用装饰器了,而要采取如下方式
app.add_url_rule('/user', None, UserView.as_view('endpoint'))

if __name__ == '__main__':
    app.run()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
自定义正则
# https://www.cnblogs.com/wupeiqi/articles/7552008.html
from flask import Flask, views, url_for
from werkzeug.routing import BaseConverter

app = Flask(import_name=__name__)

# 第一步:定制类
class RegexConverter(BaseConverter):
    """
    自定义URL匹配正则表达式
    """
    def __init__(self, map, regex):
        super(RegexConverter, self).__init__(map)
        self.regex = regex

    def to_python(self, value):
        """
        路由匹配时,匹配成功后传递给视图函数中参数的值
        """
        return int(value)  # 此处可做类型转换

    def to_url(self, value):
        """
        使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
        """
        val = super(RegexConverter, self).to_url(value)
        return val 


# 第二步:添加到转换器中
app.url_map.converters['regex'] = RegexConverter

"""
1、用户发送请求
2、flask内部进行正则匹配
3、调用to_python(正则匹配的结果)方法
4、to_python方法的返回值会交给视图函数的参数
"""

# 第三步:使用自定义正则
@app.route('/index/<regex("\d+"):nid>')
def index(nid):
    print(url_for('index', nid='888'))  # 触发to_url()方法
    return 'Index'


if __name__ == '__main__':
    app.run()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

2、session实现原理

Flask源码入口
from flask import Flask

# 1、实例化Flask对象
app = Flask(import_name=__name__)

# 2、设置路由
"""
app.url_map=[('/index', index), ('/login', login)]
"""
@app.route('/index/')
def index():
    return 'Index'

if __name__ == '__main__':
    # 3、启动socket服务器
    app.run()
    # 4、用户请求来
    # 执行app.__call__()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

app.__call__()

    def __call__(self, environ, start_response):
        # environ:是请求相关的所有数据
        # start_response:用于设置响应相关数据
        return self.wsgi_app(environ, start_response)
  • 1
  • 2
  • 3
  • 4

self.wsgi_app(environ, start_response)

flask所有源码都是在这个函数里面触发的

    def wsgi_app(self, environ, start_response):
        """
        1、获取environ并对其再次封装,得到request对象
        2、从environ中获取名称为session的cookie,解密,反序列化
        3、两个东西放到‘某个神奇’的地方:上下文管理
        """
        # ctx=RequestContext(self, environ) #self就是app对象
        # ctx.request=Request(environ) # 可以request点出来了
        # ctx.session=None
        ctx = self.request_context(environ)  # 再次封装请求
        error = None
        try:
            try:
                # 执行这句之前,ctx中已经封装了request和session了
                # 将ctx放到‘某个神奇’的地方
                # 执行SecureCookieSessionInterface.open_session,去cookie中获取值并给ctx.session重新赋值
                ctx.push() # 调用session_interface
                # 4、执行视图函数
                # 5、‘某个神奇’的地方获取session,加密,序列化,写入cookie
                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
            """
            6、‘某个神奇’的位置清空
            """
            ctx.auto_pop(error)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

3、蓝图

目标:给开发者提供目录结构

所有的视图都放在一个py文件中,管理非常不便。因此需要分目录管理。

简单目录示例
pro_flask  # 此目录要与项目名一致
	statics
    templates
    views  # 分项目写视图
    	- account.py
        - blog.py
        - user.py
    __init__.py
run.py
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

run.py:启动文件

from pro_flask import create_app
app = create_app()
if __name__ == '__main__':
    app.run()
  • 1
  • 2
  • 3
  • 4

__init__.py

from flask import Flask
# 导入蓝图
from .views.account import account
from .views.blog import blog
from .views.user import user

def create_app()
    app = Flask(__name__,template_folder='templates',static_folder='statics',static_url_path='/static')
    
    # 可以全局装饰
    @app.before_request
    def x1():
        print('app.before_request')

    # 注册蓝图:给蓝图与app绑定关系
    app.register_blueprint(account)
    app.register_blueprint(blog)
    app.register_blueprint(user, url_prefix='/api') # 加了url_prefix参数,后面访问时,在对应路由前面还要加这个前缀
    return app
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

account.py

# 导入蓝图
from flask import Blueprint
from flask import render_template
from flask import request
# 实例化蓝图,蓝图这里也可以单独指定找模板和静态文件的地方
account = Blueprint('account', __name__)

@account.route('/login.html', methods=['GET', "POST"])  # 伪静态
def login():
    return render_template('login.html')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

其他两个视图与account.py一样,只是蓝图的名字不同。

4、threading.local:与flask没有任何关系

import threading
import time
v=0

def task(i):
    global v
    v=i  # 多线程共同修改同一份数据,导致数据异常
    time.sleep(1)
    print(v)  # 最后所有数据得到的都是9

for i in range(10):
    t = threading.Thread(target=task, args=(i,))
    t.start
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

要解决上面的问题,我们原来是要给数据修改的部分加锁,今天换一种方法

threading.local()

作用:

​ 为每个线程创建一个独立的空间,使得线程对自己的空间中的数据进行操作(数据隔离),会浪费空间。

import threading
import time
v=0
obj = threading.local()

def task(i):
    obj.xx = i  
    time.sleep(2)  # 无论停几秒,结果都是一样的
    print(obj.xx)  

for i in range(10):
    t = threading.Thread(target=task, args=(i,))
    t.start
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

获取一个线程的唯一标记:threading.get_ident()

自定义数据隔离:类似threading.local()

import time
import threading

DIC = {}

def task(i):
    ident = threading.get_ident()
    if ident in DIC:
        DIC[ident]['xxx'] = i
    else:
        DIC[ident] = {'xxx': i}
    time.sleep(2)
    print(DIC[ident]['xxx'], i)  # 打印数据与索引

for i in range(10):
    t = threading.Thread(target=task, args=(i,))
    t.start
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

根据字典自定义一个为每个协程开辟空间进行存取数据(数据隔离)

import time
import threading
import greenlet

DIC = {}

def task(i):
    ident = greenlet.getcurrent()  # 获取协程的唯一标记
    if ident in DIC:
        DIC[ident]['xxx'] = i
    else:
        DIC[ident] = {'xxx': i}
    time.sleep(2)
    print(DIC[ident]['xxx'], i)  # 打印数据与索引

for i in range(10):
    t = threading.Thread(target=task, args=(i,))
    t.start
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
封装实现:比threading.local()牛逼,支持协程
import threading
try:
    import greenlet
    get_ident = greenlet.getcurrent
except:
    get_ident = threading.get_ident
    
class Local(object):
    DIC = {}  # {'123':{'xx':123, 'x1': 4}}
    
    def __getattr__(self, item):  # obj.xx的时候触发,xx要不存在
        ident = get_ident()  # 线程或协程
        if ident in self.DIC:
            return self.DIC[ident].get(item)
        return None
    
    def __setattr__(self, key, value):  # obj.xx=123触发
        ident = get_ident()  # 线程或协程
        if ident in self.DIC:
            self.DIC[ident][key]=value
        else:
            self.DIC[ident]={key:value}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/540573
推荐阅读
相关标签
  

闽ICP备14008679号