Flask基础
开篇引入
1.django和flask的相同点和不同点?
|
1
2
3
4
5
6
7
8
9
|
共同点:都是基于wsgi的不同点:(1) django是一个大而全的框架,提供了方便内置的框架,orm,admin,分页,form,model_form,缓存,信号等很多方便的组件,我们只要在配置文件中一修改就可以使用。(2) flask是一个短小精悍,可扩展性非常强,flask适合开发小型的网站,也可以开发大型网站,因为它给我们提供有许多第三方组件,我们就可以结合这些第三方组件集成一个像django一样拥有很多功能的web框架。可定制性非常强。(3) flask和django最大的不同点:request/sessionflask是直接导入的,request在全局起作用。django是依附于request参数,通过参数传导。两个框架没有优劣之分,具体应用看实际需求。 |
2.什么是wsgi?
|
1
2
3
4
5
|
web服务网关接口,wsgi是一个协议,实现该写一个的模块:- wsgiref- werkzeug实现协议的模块本质上就是socket服务端用于接收用户请求,并处理。一般web框架基于wsgi实现,这样实现关注点分离,主要负责业务处理。 |
1 from wsgiref.simple_server import make_server
2
3 def run_server(environ, start_response):
4 start_response('200 OK', [('Content-Type', 'text/html')])
5 return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ]
6
7
8 if __name__ == '__main__':
9 httpd = make_server('127.0.0.1', 8000, run_server)
10 httpd.serve_forever()
1 from werkzeug.wrappers import Response
2 from werkzeug.serving import run_simple
3
4 def run_server(environ, start_response):
5 response = Response('hello')
6 return response(environ, start_response)
7
8 if __name__ == '__main__':
9 run_simple('127.0.0.1', 8000, run_server)
from werkzeug.wrappers import Response
from werkzeug.serving import run_simple
class Flask(object):
def __call__(self,environ, start_response):
response = Response('hello')
return response(environ, start_response)
def run(self):
run_simple('127.0.0.1', 8000, self)
app = Flask()
if __name__ == '__main__':
app.run()
基础知识
安装:
pip3 install flask
特点: 短小精悍、可扩展强 的一个Web框架。
特色:上下文管理机制 wsgi:web service getway interface web服务网管接口 依赖wsgi:werkzurg
from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple
def run(environ,start_response):
response = Response('hello')
return response(environ, start_response)
if __name__ == '__main__':
run_simple('localhost', 4000, run)
from werkzeug.wrappers import Request, Response
@Request.application
def hello(request):
return Response('Hello World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, hello)
一、配置文件
1.使用:所有配置都在app.config中
- 方式一:app.config['DEBUG'] = True
PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...)
- 方式二:app.config.from_object('类的路径')
- 方式三:app.config.from_pyfile('py文件路径')
class Config(object):
DEBUG = False
TESTING = False
DATABASE_URI = 'sqlite://:memory:'
class ProductionConfig(Config):
DATABASE_URI = 'mysql://user@localhost/foo'
class DevelopmentConfig(Config):
DEBUG = True
class TestingConfig(Config):
TESTING = True
2.实现原理 指定一个字符串(类的路径/文件路径),importlib-->getattr 找到这个类/模块把这个类/模块的所有字段(大写)一个一个获取,获取的时候判断isupper(),给一个路径'settings.Foo',可以找到类并获取其中大写的静态字段。
import importlib
path = 'settings.Foo'
p,c = path.rsplit('.',maxsplit=1)
m = importlib.import_module(p)
# m = __import__(p)
cls = getattr(m,c)
for key in dir(cls):
if key.isupper():
print(key,getattr(cls,key))
'ENV': None,
'DEBUG': None, 是否开启DEBUG模式
'TESTING': False, 是否开启测试模式
'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
'SECRET_KEY': None,
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
'USE_X_SENDFILE': False,
'SERVER_NAME': None,
'APPLICATION_ROOT': '/',
'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'SESSION_COOKIE_SAMESITE': None,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS': None,
'TRAP_HTTP_EXCEPTIONS': False,
'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http',
'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True,
'JSONIFY_PRETTYPRINT_REGULAR': False,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
'MAX_COOKIE_SIZE': 4093,
})
二、路由系统
重点:基于装饰器实现
技术点:-functools.wraps(func):保留原函数的原信息
装饰器(带参数)
- methods=['GET','POST']
- endpoint:反向生成url
- url_for(endpoint)
自定义装饰器放下面 注意事项:
- - endpoint默认是函数名
- - 不要让endpoint重名,如果重名函数也一定要相同。
- - 加装饰器时要加functools.wraps(func) + functools.partial
# -*- coding: utf-8 -*-
"""
@Datetime: 2018/12/21
@Author: Zhang Yafei
"""
"""1.装饰器"""
from functools import wraps
def auth(func):
@wraps(func) # 伪装的更彻底
def inner(*args,**kwargs):
print('前')
ret = func(*args,**kwargs)
print('后')
return ret
return inner
@auth
def index():
print('index')
@auth
def detail():
print('index')
print(index.__name__)
print(detail.__name__)
"""2.endpoint默认是函数名"""
路由设置的两种方式
方式一
@app.route('/xxx')
def index():
return "index"
方式二
def index():
return "index"
app.add_url_rule("/xxx",None,index)
-动态路由
/index/<int:nid>
def index(nid):
print(nid)
return 'index'
1 rule, URL规则
2 view_func, 视图函数名称
3 endpoint=None, 名称,用于反向生成URL,即: url_for('名称')
4 methods=None, 允许的请求方式,如:["GET","POST"]
5 strict_slashes=None, 对URL最后的 / 符号是否严格要求,
6 redirect_to=None, 重定向到指定地址
7
8 defaults=None, 默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数
9 subdomain=None, 子域名访问
from flask import Flask,url_for
app = Flask(__name__)
# 步骤一:定制类
from werkzeug.routing import BaseConverter
class RegexConverter(BaseConverter):
"""
自定义URL匹配正则表达式
"""
def __init__(self, map, regex):
super(RegexConverter, self).__init__(map)
self.regex = regex
def to_python(self, value):
"""
路由匹配时,匹配成功后传递给视图函数中参数的值
:param value:
:return:
"""
return int(value)
def to_url(self, value):
"""
使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
:param value:
:return:
"""
val = super(RegexConverter, self).to_url(value)
return val
# 步骤二:添加到转换器
app.url_map.converters['reg'] = RegexConverter
"""
1.用户发送请求
2.flask内部进行正则匹配
3.调用to_python(正则匹配的结果)方法
4.to_python方法的返回值会交给视图的函数
"""
# 步骤三:使用自定义正则
@app.route('/index/<reg("\d+"):nid>')
def index(nid):
print(nid,type(nid))
print(url_for('index',nid=987))
return 'index'
if __name__ == '__main__':
app.run()
@main.route('/data_list/<table_name>')
def data_list(table_name):
df = reader.enable_admins[table_name]
page_obj = Pagination(df['data'].shape[0], request.args.get('p'),per_page_num=5)
page_str = page_obj.page_str(base_url=url_for('main.data_list', table_name=table_name))
admin_class = df['admin_class']
data_list = df['data'][page_obj.start:page_obj.end]
context = {'table_name':table_name, 'data_list':data_list,
'page_obj':page_obj, 'page_str':page_str,
'admin_class':admin_class,
}
return render_template('data_list.html', **context)
三、视图
视图:FBV和CBV
技术点:反射
视图函数中获取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)
import functools
from flask import Flask,views
app = Flask(__name__)
def wrapper(func):
@functools.wraps(func)
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
class UserView(views.MethodView):
methods = ['GET']
decorators = [wrapper,]
def get(self,*args,**kwargs):
return 'GET'
def post(self,*args,**kwargs):
return 'POST'
app.add_url_rule('/user',None,UserView.as_view('uuuu'))
if __name__ == '__main__':
app.run()
四、请求相关
技术点:面向对象的封装
request属性
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# 请求相关信息 # request.method 请求方法 GET/POST # request.args GET请求参数 # request.form POST请求参数 # request.values GET和POST请求参数 # request.cookies cookie值 # request.headers 请求头信息 # request.path 请求地址路径 # request.full_path 请求地址全路径 # request.script_root # request.url # request.base_url # request.url_root # request.host_url # request.host # request.files # obj = request.files['the_file_name'] # obj.save('/var/www/uploads/' + secure_filename(f.filename)) |
print('headers:', request.headers)
print('cookies:', request.cookies)
print('path:', request.path)
print('full_path:', request.full_path)
print('url:', request.url)
print('script_root:', request.script_root)
print('base_url:', request.base_url)
print('url_root:', request.url_root)
print('host:', request.host)
print('host_url:', request.host_url)
headers: Host: 172.25.0.246:8999
Connection: keep-alive
Content-Length: 37
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 S
afari/601.1 wechatdevtools/1.02.1901230 MicroMessenger/6.7.3 Language/zh_CN webview/ token/a46ac17280ff1fcb82b684b2084ee168
Origin: http://127.0.0.1:23672
Authorization: f3c4e30debf4030ace5c3cdf40e6332d#1
Content-Type: application/x-www-form-urlencoded
Accept: */*
Referer: https://servicewechat.com/wx49bec3712cb29bf5/devtools/page-frame.html
Accept-Encoding: gzip, deflate
cookies: {}
path: /api/member/check-reg
full_path: /api/member/check-reg?
url: http://172.25.0.246:8999/api/member/check-reg
script_root:
base_url: http://172.25.0.246:8999/api/member/check-reg
url_root: http://172.25.0.246:8999/
host: 172.25.0.246:8999
host_url: http://172.25.0.246:8999/
@main.route('/data_list/<table_name>')
def data_list(table_name):
df = reader.enable_admins[table_name]
page_obj = Pagination(df['data'].shape[0], request.args.get('p'),per_page_num=5)
page_str = page_obj.page_str(base_url=url_for('main.data_list', table_name=table_name))
admin_class = df['admin_class']
data_list = df['data'][page_obj.start:page_obj.end]
context = {'table_name':table_name, 'data_list':data_list,
'page_obj':page_obj, 'page_str':page_str,
'admin_class':admin_class,
}
return render_template('data_list.html', **context)
@ac.route('/login',methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
username = request.form.get('username')
password = request.form.get('pwd')
"""数据库验证"""
password = get_md5(password)
# conn = Connect(host='localhost', user='root', password='0000', database='flask_code', charset='utf8')
# cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 每一行是字典
# cursor.execute("select id,username,nickname from userinfo where username=%(us)s and password=%(pw)s",{'us':username,'pw':password})
# data = cursor.fetchone()
data = fetchone("select id,username,nickname from userinfo where username=%(us)s and password=%(pw)s",{'us':username,'pw':password})
if not data:
return render_template('login.html',error='用户名或密码错误')
session['user_info'] = {'id':data['id'],'username':data['username'],'nickname':data['nickname']}
if request.form.get('remember'):
session.permanent = True
ac.permanent_session_lifetime = timedelta(days=31)
return redirect('/home')
五、响应相关
技术点:面向对象的封装
响应体:4种
return '欢迎使用'
return jsonify({'k1':'v1'})
return render_template('xxx.html')
return redirect('/index')
定制响应头
obj = make_response(render_template('index.html'))
obj.headers['xxxxx'] = '123'
obj.set_cookie('key','value')
return obj
示例程序:用户访问限制
@app.route('/detail/<int:nid>')
def detail(nid):
if not session.get('user'):
return redirect(url_for('login'))
info = STUDENT_DICT[nid]
return render_template('detail.html', info=info)
def auth(func):
@wraps(func)
def inner(*args,**kwargs):
if not session.get('user'):
return redirect(url_for('login'))
ret = func(*args,**kwargs)
return ret
return inner
@app.route('/index')
@auth
def index():
return render_template('index.html',stu_dict=STUDENT_DICT)
应用场景:比较少的函数中需要添加额外的功能
@app.before_request
def xxxx():
if request.path == '/login':
return None
if not session.get('user'):
return None
return redirect(url_for('login'))
应用场景:比较多的函数中需要添加额外的功能
六、模板渲染
基本数据类型:可以执行python语法,如:dict.get() list['xx']
控制代码块:条件语句和循环语句
- if/else if /else / endif
- for / endfor
<ul class="pagination">
{% if page_obj.num_pages > 1 %}
{{ page_str }}
{% else %}
{% endif %}
</ul>
<table class="table table-bordered">
<thead>
<tr style="">">
<th>序号</th>
<th>文件名</th>
<th>样本数</th>
<th>特征数</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for k,v in data_list.items() %}
<tr>
<td>{{ loop.index }}</td>
<td><a href="{{ url_for('main.data_list',table_name=k)}}">{{ k }}</a></td>
<td>{{ v['data'].shape }}</td>
<td>{{ v['data'].shape }}</td>
<td><a href="{{ url_for('main.data_list',table_name=k)}}">查看</a></td>
</tr>
{% endfor %}
</tbody>
</table>
除此之外,还有一个特殊的循环函数loop

-
在循环内部,你可以使用一个叫做loop的特殊变量来获得关于for循环的一些信息
-
比如:要是我们想知道当前被迭代的元素序号,并模拟Python中的enumerate函数做的事情,则可以使用loop变量的index属性,例如:
-
{% for post in posts%} {{loop.index}}, {{post.title}} {% endfor %} 会输出这样的结果 1, Post title2, Second Post - cycle函数会在每次循环的时候,返回其参数中的下一个元素,可以拿上面的例子来说明:
-
{% for post in posts%} {{loop.cycle('odd','even')}} {{post.title}} {% endfor %} 会输出这样的结果: odd Post Title even Second Post
传入函数
- -django,自动执行
- -flask,不自动执行
自定义函数
@app.template_global()
def sb(a,b):
return a+b
@app.template_filter() # 适合if 判断
def db(a,b,c):
return a+b+c
html
{{ 1|db(2,3) }}
{% if 1|db(2,3) %}
<div>666</div>
{% else %}
<div>999</div>
{% endif %}
@app.template_filter() # 适合if 判断
def db(a,b,c):
return a+b+c
用装饰器来实现自定义过滤器。装饰器传入的参数是自定义的过滤器名称。
@app.template_filter('lireverse')
def do_listreverse(li):
# 通过原列表创建一个新列表
temp_li = list(li) # 将新列表进行返转
temp_li.reverse() return temp_li
在 html 中使用该自定义过滤器
<br/> my_array 原内容:{{ my_array }}<br/> my_array 反转:{{ my_array | lireverse }}
运行结果
my_array 原内容:[3, 4, 2, 1, 7, 9]
my_array 反转:[9, 7, 1, 2, 4, 3]
模板继承
layout.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
tpl.html
{% extends 'layout.html' %}
{% block content %}
{% include 'form.html' %}
{% macro ccccc(name,type='text', value='') %}
<h1>宏</h1>
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
<input type="submit" name="提交">
{% endmacro %}
{{ ccccc('n1') }}
{{ ccccc('n2') }}
{% endblock %}
<include 'from.html'>
form.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>sasasasa sasasasasasa</div>
</body>
</html>
宏
{% macro ccccc(name,type='text', value='') %}
<h1>宏</h1>
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
<input type="submit" name="提交">
{% endmacro %}
{{ ccccc('n1') }}
{{ ccccc('n2') }}
安全
前端:{{ txt|safe }}
后端:Markup(txt)
七、session
原理:加密后放置在用户浏览器的cookie中
流程:
请求到来:flask读取cookie中的session对应的值:eyJrMiI6NDU2LCJ1c2VyIjoiZmVpIn0,将该值解密并反序列化成字典,放入内存以便视图函数使用。
视图函数:
@app.route('/sess')
def sess():
print(session.__class__)
session['k1'] = 123
session['k2'] = 456
del session['k1']
return 'Session'
请求结束,flask会读取内存中字典的值,进行序列化+加密,写入用户cookie中。
实现原理(源码)
def wsgi_app(self, environ, start_response):
"""
1.获取environ并对其进行再次封装
2.从environ中获取名称为session的cookie,解密,反序列化
3.两个东西放到‘某个神奇'的地方
"""
# ctx = RequestContext(self, environ) #self是app对象,evviron是原始数据对象
# ctx.request = Request(environ)
# ctx.session = None
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
# 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)
闪现(flash): 在session存储一个数据,读取时通过pop将数据删除,形成一种数据只能取一次的效果
from flask import Flask,flash,get_flashed_messages
@app.route('/page1')
def page1():
flash('临时数据存储','error')
flash('sasasasa','error')
flash('sasasas','info')
return 'Session'
@app.route('/page2')
def page2():
print(get_flashed_messages(category_filter=['error']))
return 'Session'
八、中间件
一般不常用,因为它的执行顺序很靠前,无序获取request对象,与请求相关信息难以获得
- call方法什么时候触发?
-用户发起请求时,才执行
- -任务:在执行call方法之前,做一个操作,call方法执行之后,做一个操作
class Middleware(object):
def __init__(self,old):
self.old = old
def __call__(self, *args, **kwargs):
print('前')
ret = self.old(*args,**kwargs)
print('后')
return ret
if __name__ == '__main__':
app.wsgi_app = Middleware(app.wsgi_app)
app.run()
九、特殊的装饰器
技术点:before_request和after_request的实现原理
六大装饰器
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
before_first_requestbefore_request 视图函数之前,原理是将视图函数放入到一个列表中,循环,如果有返回值停止循环,后面的函数也将不会执行after_request 视图函数之后,原理是将视图函数放入到一个列表中reverse,循环执行template_globaltemplate_filtererrorhandler @app.errorhandler(404) def not_found(arg): print(arg) return '404 没找到' |
# -*- coding: utf-8 -*-
"""
@Datetime: 2018/12/21
@Author: Zhang Yafei
"""
from flask import Flask
app = Flask(__name__)
@app.before_first_request
def x():
print('before_first')
@app.before_request
def x1():
print('before:x1')
@app.before_request
def xx1():
print('before:xx1')
@app.after_request
def x2(response):
print('after:x2')
return response
@app.after_request
def xx2(response):
print('after:xx2')
return response
@app.route('/index')
def index():
print('index')
return 'index'
@app.route('/order')
def order():
print('order')
return 'order'
@app.errorhandler(404)
def not_found(arg):
print(arg)
return '404 没找到'
if __name__ == '__main__':
app.run()
十、蓝图
(1) 目标:目录结构的划分 (2) 自定义模板、静态文件
admin = Blueprint(
'admin',
__name__,
template_folder='templates',
static_folder='static'
)
(3) 给某一类url添加前缀变量 app.register_blueprint(admin, url_prefix='/admin') (4) 给一类url添加before_request
@app.before_request
def x1():
print('app.before_request')
十一、多app
from flask import Flask
from werkzeug.wsgi import DispatcherMiddleware
from werkzeug.serving import run_simple
app1 = Flask("app1")
app1.config['DB'] = 123
app2 = Flask("app2")
app1.config['DB'] = 456
@app1.route('/web')
def web():
print('web')
return '12213'
@app1.route('/news')
def news():
print('news')
return '12213'
@app2.route('/admin')
def admin():
print('admin')
return '12213'
@app2.route('/article')
def article():
print('article')
return '12213'
"""
/web
/new
/app2/admin
/app2/article
"""
app = DispatcherMiddleware(app1, {
'/app2': app2,
})
if __name__ == '__main__':
run_simple(hostname='127.0.0.1', port=5000, application=app)
from multi_app import app1
from multi_app import app2
with app1.app_context():
pass # 为app1创建数据库
with app2.app_context():
pass # 为app2创建数据库
什么是响应式布局?
技术点:@media
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" />
</head>
<body>
<div class="row" style="">>
<div class="col-lg-6">.col-lg-6</div>
<div class="col-lg-6">.col-lg-6</div>
</div>
<div class="row" style="">>
<div class="col-md-6">.col-md-6</div>
<div class="col-md-6">.col-md-6</div>
</div>
<div class="row" style="">>
<div class="col-sm-6">.col-sm-6</div>
<div class="col-sm-6">.col-sm-6</div>
</div>
<div class="row" style="">>
<div class="col-xs-6">.col-xs-6</div>
<div class="col-xs-6">.col-xs-6</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body{
margin: 0;
}
.pg{
width: 100%;
background-color: rebeccapurple;
}
@media (min-width: 666px) {
.pg{
background-color: green;
}
}
@media (min-width: 888px) {
.pg{
background-color: red;
}
}
</style>
</head>
<body>
<div>
<div class="pg">asdfasdf</div>
</div>
</body>
</html>
注:
1. 上下文管理的实现?
当请求到来的时候,
flask会把请求相关和session相关信息封装到一个ctx = RequestContext对象中,
会把app和g封装到app_ctx = APPContext对象中去,
通过localstack对象将ctx、app_ctx对象封装到local对象中
获取数据(执行视图函数的时候)
通过导入一个模块,用模块.的方式获取我们想要的数据
实现细节:
通过localproxy对象+偏函数,调用localstack去local中获取对应的响应ctx、app_ctx中封装值
问题:为什么要把ctx = request/session app_ctx = app/g
因为离线脚本需要使用app_ctx
请求结束
调用localstk的pop方法,将ctx和app_ctx移除
2. 为什么把上下文管理分成:
- 应用上下文:request/session
- 请求上下文: app/g
离线脚本应用
3. Local的作用?
类似于threading.local的作用,但是是他的升级版(greentlet.get_current())
__storage__ = {
1231: {},
1231: {}
}
4. LocalStack作用?
将Local中__storage__的值维护成一下结构:
__storage__ = {
1231: {stack:[],},
1231: {stack:[],}
}
5. 为什么要维护成一个栈?
6. 为什么导入request,就可以使用?
每次执行request.xx方法时,会触发LocalProxy对象的__getattr__等方法,由方法每次动态的使用
LocalStack去Local中获取数据
posted on 2019-04-27 21:03 torotoise512 阅读(125) 评论(0) 收藏 举报

浙公网安备 33010602011771号