flask学习一
@(python之路)[flask学习一]
flask学习一
flask简单使用
装饰器
装饰器基础:
def wapper(func):
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
'''
1. 立即执行wapper函数,并将下面装饰的函数单座参数传递
2. 将wapper函数返回值获取,在index赋值
'''
@wapper
def index():
print("函数内容")
# 这里实际执行的inner函数,inner函数内部调用原函数
index()
装饰器晋升:
这里我们引入一个functools模块,这个模块是它能将装饰过的函数的特殊属性保留。
import functools
def wapper(func):
@functools.wraps(func)
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
@wapper
def index():
print("hello world ,from index")
@wapper
def order():
print("hell world ,from order")
print(index.__name__)
print(order.__name__)
########## 使用functools.wraps(函数) #############
index
order
########## 不使用functools.wraps(函数)#####################
inner
inner
注:
对于这个functools.wraps装饰器,相当于wrapper = functools.wraps(func)(wrapper)。
面像对象的封装
- 将数据封装到对象中
- 对同一类方法封装到类中
什么后面可以添加括号
def f1():
print('f1')
class F2(object):
pass
class F3(object):
def __init__(self):
pass
def ff3(self):
print('ff3')
class F4(object):
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('f4')
def func(arg)
arg()
由于arg在函数中加括号,所以他只有4中表现形式
函数:
# 1、函数,内部执行函数
func(f1)
类:
# 2、类,内部执行__init__方法
func(F2)
方法:
# 3、 方法,obj.ff3
obj1 = F3()
func(obj1.ff3)
对象:
# 4、 对象
obj2 = F4()
func(obj2)
什么是对象
对象 = 属性(特征)+ 方法(行为)
class F4(object):
def __init__(self):
print("构造方法")
def __call__(self, *args, **kwargs):
print("F4")
obj = F4()
obj()
######### 显示 #########
构造方法
F4
web框架本质
web框架的本质就是socket
import socket
def main():
sock = socket.socket()
sock.bind(("localhost",80))
sock.listen(5)
while True:
connection , address = sock.accept()
buf = connection.recv(1024)
connection.send(b"HTTP/1.1 200 ok\r\n\r\n")
connection.send(b"No No No")
connection.close()
if __name__ == '__main__':
main()
wsgi
Fask是基于werkzeug模块实现了wsgi协议(规范模块)
djang是基于wsgiref
代码上线: 一般我们在正式环境上使用的是uwsgi
flask入门
使用Flask内部的werkzeug模块实现wsgi协议(模块)的规范
基于函数
from werkzeug.wrappers import Request,Response
from werkzeug.serving import run_simple
@Request.application
def hello(request):
return Response("no no no ")
if __name__ == '__main__':
run_simple("localhost",4000,hello)
使用Flask实现简单的访问页面
示例1:
from flask import Flask
# 1. 实例化Flask对象
app = Flask('xxxx')
"""
1. 执行 app.route('/index')并获取返回值 xx
2.
@xx
def index():
return 'Hello World'
3. 执行 index = xx(index)
本质:
{
'/index': index
}
"""
@app.route('/index')
def index():
return 'Hello World'
if __name__ == '__main__':
app.run()
源码分析:
app.run()
到底做了什么?
我们查看一下app.run方法
def run(self, host=None, port=None, debug=None, **options):
from werkzeug.serving import run_simple
if host is None:
host = '127.0.0.1'
if port is None:
server_name = self.config['SERVER_NAME']
if server_name and ':' in server_name:
port = int(server_name.rsplit(':', 1)[1])
else:
port = 5000
if debug is not None:
self.debug = bool(debug)
options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug)
try:
# 这里就是app = Flask(),然后将app传入;
# run_simple(host,port,self,**options)
# 这里的self就是,app = Flask();在回顾一下什么情况下执行(),函数,对象,方法,类;app就是对象()
# 所以在他源码的请求入口就是__call__方法;也就是Flask框架的入口;
# def __call__(self,environ,start_response):
# return self.wsgi_app(environ,start_response)
run_simple(host, port, self, **options)
finally:
self._got_first_request = False
示例2:
from flask import Flask,render_template,request
# 在Flask中将所有的请求信息都凡在了request中,这里和django不一样,
# 所以我们在使用的时候需要导入request;
# 在Flask中也有render方法和django类似,只不过这里需要定义模板路径,
# 所以我们在实例化Flask中定义,template_folder中定义模板路径,这里
# 我写的是默认路径
# 有一些相关的设置需要看一下Flask()源码
# __init__(selef,……)
app = Flask("xxx",template_folder="templates")
# 如果在Flask中想让其支持其他请求,就需要在@app.route()中添加
# methods=["GET","POST"] 这样的化就支持了get和post请求
@app.route("/login",methods=["GET","POST"])
def login():
if request.method == "GET":
print(123)
return render_template("login.html")
else:
print(456)
user = request.form.get("user")
pwd = request.form.get("pwd") # from 请求体
if user == "tom" and pwd == "123":
return "登陆成功"
# return render_template("login.html",msg="用户名密码错误")
# 也可以用字典的方式传入参数()jinja2
return render_template("login.html",**{"msg":"用户名密码错误"})
if __name__ == '__main__':
app.run()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登陆页面</h1>
<form method="post">
<input type="text" name="user">
<input type="password" name="pwd">
<input type="submit" value="提交">{{msg}}
</form>
</body>
</html>
示例3:
from flask import Flask,render_template,request,session,redirect
import functools
app = Flask("xxx",template_folder="templates")
# 这里的session是需要自己写,相比较django,我们可以调用django的内置方法使用;
# 这里需要一个secrt key将产生的session随机字符串放到浏览器中;
app.secret_key = "flkjalsfjlaksjflsafjslfj"
def auth(func):
@functools.wraps(func)
def inner(*args,**kwargs):
user_info = session.get("user_info")
if not user_info:
return redirect("/login")
return func(*args,**kwargs)
return inner
@app.route("/index",methods=["GET",])
@auth
def index():
return "is index"
@app.route("/login",methods=["GET","POST"])
def login():
if request.method == "GET":
print(123)
return render_template("login.html")
else:
print(456)
user = request.form.get("user")
pwd = request.form.get("pwd") # from 请求体
if user == "tom" and pwd == "123":
session["user_info"] = user
return "登陆成功"
# return render_template("login.html",msg="用户名密码错误")
# 也可以用字典的方式传入参数()jinja2
return render_template("login.html",**{"msg":"用户名密码错误"})
@app.route("/logout",methods=["GET"])
def logout():
# 删除session
del session["user_info"]
return redirect("/login")
if __name__ == '__main__':
app.run()
Flask配置文件
flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
{
'DEBUG': get_debug_flag(default=False), 是否开启Debug模式
'TESTING': False, 是否开启测试模式
'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
'SECRET_KEY': None,
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
'USE_X_SENDFILE': False,
'LOGGER_NAME': None,
'LOGGER_HANDLER_POLICY': 'always',
'SERVER_NAME': None,
'APPLICATION_ROOT': None,
'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS': False,
'TRAP_HTTP_EXCEPTIONS': False,
'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http',
'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True,
'JSONIFY_PRETTYPRINT_REGULAR': True,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
}
导入配置文件:
方式一:
app.config["DEBUG"] = True
由于config对象本质上就是字典,所以还可以 使用app.config.update(……)
方式二:
创建一个setting.py文件
class BaseConfig(object):
DEBUG = False
SESSION_REFRESH_EACH_REQUEST = True
class ProConfig(BaseConfig)
pass
class DevConfig(BaseConfig):
DEBUG = True
我们在配置文件可以调用,
app.config.from_object("settings.DevConfig")
这里他是用配置文件的形式来处理,我们可以参照这种方式来写文件。
例如:我们写cmdb我们需要发送报警信息。
我们建立一个目录为notify,在这个目录下我们填写报警模块,email、msg和wechat,
#email.py
class Email(object):
def send(self):
print('发送邮件提醒')
#msg.py
class Msg(object):
def send(self):
print('发送短信提醒')
#wechat.py
class Wechat(object):
def send(self):
print('发送微信')
这些就是我们要编写的文件
在notify目录下我们需要在__init__.py文件中,将setting文件中的信息提取出来。
#__init__.py
import settings
import importlib
def send_notify():
for path in settings.NOTIFY_LIST:
# 'notify.email.Email',
# 'notify.msg.Msg',
module_path,cls_name = path.rsplit('.',maxsplit=1)
# m = importlib.import_module("notify.email") # import notify.email
m = importlib.import_module(module_path)
# 我们通过反射的方式找到这个类,并实例化
cls = getattr(m,cls_name)
obj = cls()
obj.send()
在notify的同级目录下定义settings.py,就像django的中间件一样。
# settings.py
NOTIFY_LIST = [
# 这里的Email就是类;
'notify.email.Email',
'notify.wechat.Wechat',
'notify.msg.Msg',
]
我们书写启动接口;还是notify的同级目录
# run.py
from notify import send_notify
def run():
send_notify()
if __name__ == '__main__':
run()
这里我们是仿照django中间价的形式,书写配置文件。当我们添加新的报警机制模块是,就可以像django的中间件添加
路由系统
添加路由的本质
闭包
import functools
from flask import Flask
# 配置:模板/静态文件
app = Flask('xxxx',template_folder="templates")
"""
{
'/index': index函数
}
1. decorator = app.route('/index')
2.
@decorator
def index():
return "index"
3. decorator(index)
"""
# 遵循从上到下执行,执行route
@app.route('/index')
def index():
return "index"
def order():
return 'Order'
# 这里我们根据下方源码分析可得;
app.add_url_rule('/order', None, order)
if __name__ == '__main__':
app.run()
注意:
route路由系统的本质就是一个闭包,在里边可以。
源码分析
def route(self, rule, **options):
# 创建一个闭包把rule = /index 和
# self = app对象封装到route中。
def decorator(f):
# f = index函数
# endpoint = None 默认
endpoint = options.pop('endpoint', None)
# 把index、endpoint、函数添加到字典里
# 它的本质就是调用自己的app(self),add_url_rule;这里rule是index函数,endpoint默认为None
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
根据上面代码分析,我们得出结论路由的本质就是self.add_url_rule(rule, endpoint, f, **options)
(self = app),无论是谁都会走到这里。下面我们就从源码分析一下内部原理。
self.add_url_rule(rule, endpoint, f, **options)
源码分析
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
# 把 rule/index/m endpoint=None view_func=函数
# 如果endpoint为空的化就创建一个函数,这个函数就是我们传进来的函数。
if endpoint is None:
'''
我们来看一下_endpoint_from_view_func(view_func)做了什么
def _endpoint_from_view_func(view_func):
assert view_func is not None, 'expected view func if endpoint ' \
'is not provided.'
return view_func.__name__
这里返回的函数名.__name__;就是函数的名称;
'''
endpoint = _endpoint_from_view_func(view_func)
options['endpoint'] = endpoint
# 这里的methods就是函数所支持的方法。
methods = options.pop('methods', None)
if methods is None:
methods = getattr(view_func, 'methods', None) or ('GET',)
if isinstance(methods, string_types):
raise TypeError('Allowed methods have to be iterables of strings, '
'for example: @app.route(..., methods=["POST"])')
methods = set(item.upper() for item in methods)
required_methods = set(getattr(view_func, 'required_methods', ()))
provide_automatic_options = getattr(view_func,
'provide_automatic_options', None)
if provide_automatic_options is None:
if 'OPTIONS' not in methods:
provide_automatic_options = True
required_methods.add('OPTIONS')
else:
provide_automatic_options = False
methods |= required_methods
# 我们看下一url_rule_class到底做了什么
# 这里相当于我们把rule = Rule(rule=/index/,endpoint=None,view_func=函数)
# 封装到这个类中
rule = self.url_rule_class(rule, methods=methods, **options)
rule.provide_automatic_options = provide_automatic_options
# 这里的url_map相当于一个大字典,把每一个路哟关系放到字典里
# 可以理解这里就是一个列表,这里就相当于存储的是好多个rule对象,
# 存储所有的路由关系
# 例如:
"""
Map() = [
Rule(rule=/index endpoint=None view_func=函数),
Rule(rule=/order endpoint=None view_func=order),
]
这里具体的化我们可以看它的url_map源码。再查看map做了什么,找到它的app;
def add(self._rules):
pass
我们着重看这里,self._rules.append(rule);
他把对象传到一个列表里,这个列表在def __init__()方法下的self._rules=[]中
"""
self.url_map.add(rule)
if view_func is not None:
old_func = self.view_functions.get(endpoint)
if old_func is not None and old_func != view_func:
raise AssertionError('View function mapping is overwriting an '
'existing endpoint function: %s' % endpoint)
self.view_functions[endpoint] = view_func
基于CBV书写
from flask import Flask,views
app = Flask("xxxx",template_folder="templates")
# 例1:
class TestView(views.View):
methods = ['GET']
def dispatch_request(self):
return 'test!'
app.add_url_rule('/test', view_func=TestView.as_view(name='test')) # name=endpoint
# app.add_url_rule('/test', view_func=view函数) # name=endpoint
def auth(func):
def inner(*args, **kwargs):
print('before')
result = func(*args, **kwargs)
print('after')
return result
return inner
# 例2:
class X1View(views.MethodView):
methods = ['GET','POST']
# 如果想要加装饰器的话,我们可以直接把装饰器添加到decorators 这个列表里,注意“,”;
decorators = [auth, ]
def get(self):
return 'x1.GET'
def post(self):
return 'x1.POST'
app.add_url_rule('/x1', view_func=X1View.as_view(name='x1')) # name=endpoint
if __name__ == '__main__':
app.run()
源码分析:
这里我们分析例2:views.MethodView
class MethodView(with_metaclass(MethodViewType, View)):
def dispatch_request(self, *args, **kwargs):
# 这里通过request.method.lower()找到请求方法;
# 通过反射执(getattr)执行请求下的方法;
meth = getattr(self, request.method.lower(), None)
if meth is None and request.method == 'HEAD':
meth = getattr(self, 'get', None)
assert meth is not None, 'Unimplemented method %r' % request.method
# 找到方法后一执行,如果事get方法就执行get.
return meth(*args, **kwargs)
这里就是路由的本质
Flask之源码流程基本分析
from flask import Flask
app = Flask("xxx",)
@app.route("/index")
def index():
return "index"
def order():
return "Order"
app.add_url_rule("/order",None,order)
if __name__ == '__main__':
# app.__call__()
app.run()
源码分析:
我们 已经在前边分析到,flask的请求入口时__call__方法。所以,我们看一它的__call__做了什么?
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`."""
# Flask框架的请求入口
# environ用户请求相关数据
# start_response 用户响应数据
return self.wsgi_app(environ, start_response)
我们把请求来的 数据转交给wsgi_app去处理;所以,Flask的代码开始是从这里
def wsgi_app(self, environ, start_response):
# environ请求相关的数据
# 我们就可以把相关的数据提取出来
# 这里我们需要知道request_context都做了什么;
'''
def request_context(self, environ):
return RequestContext(self, environ)
'''
# 类加构造传了两个值,构造方法一般用于封装;
# 这里的self就是我们的请求对象就是app得数据,
# ctx = RequestContext对象(app对象,请求相关所有数据)
我们需要分析一下RequestContext到底做了什么,
'''
def __init__(self, app, environ, request=None):
self.app = app
if request is None:
# request = Request(environ)
request = app.request_class(environ)
self.request = request
self.url_adapter = app.create_url_adapter(self.request)
self.flashes = None
self.session = None
……
# 路由匹配;获取 用户请求的url跟url_map中的对应关系进行比较
# 接下来就是执行rul,在接下来就是执行读url_map
self.match_request()
'''
# 这里分装了好多东西,request我们没有传,如果request=None
# app.request_class(environ)
# 这里的request_class可能是个类,类加()实例化;
# 这里是把请求相关的东西又做了一层封装。
# 所以就是封装封装在封装。ctx里封装了app和request;
# 而request又封装了请求相关的内容
# 路由匹配
ctx = self.request_context(environ)
# 将请求放在特殊的位置
ctx.push()
error = None
try:
try:
# 这里就是找到视图函数并执行
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
源码流程:
- a、生成路由关系
[Rule("index,函数,
Rule("index",函数,]- b、用户请求来了
获取用户请求,并进行匹配
讲ctx(分装了请求相关所有数据的对象)放置到“特殊的位置“- c、执行视图函数,也就是我们写的代码
- d、将函数的返回值返回给用户
- e、最后讲ctx在特殊的位置移除。
PS:所有框架的流程都是相似的
Flask之before_request和after_request
import functools
from flask import Flask,render_template,request,redirect,session
app = Flask('xxxx',template_folder="templates")
app.secret_key = 'as923lrjks9d8fwlkxlduf'
@app.before_request
def bf1():
print('before_request1')
# return "BF1"
@app.before_request
def bf2():
print('before_request2')
@app.after_request
def af1(response):
print('after_request1')
return response
@app.after_request
def af2(response):
print('after_request2')
return response
# app.before_request_funcs = {None: [bf1,bf2]}
# app.after_request_funcs = {None: [af1,af2]}
@app.route('/index',methods=['GET'])
def index():
print('index')
return "index"
if __name__ == '__main__':
app.run()
########### 打印结果 ##############
before_request1
before_request2
index
after_request2
after_request1
注意:
@app.before_request
和@app.after_request
这里需要注意,befor在执行视图之前执行,after在执行完视图之后执行。特别注意在befor装饰器下的函数不能又return值。从他的打印结果我们可以总结出请求进入时候,先执行顺序。这里跟django(1.10以后)又区别,例如:django的中间件,访问开始当中间件走到第三个时候,中间件验证不通过,直接从第三个中间件返回给用户。而Flask却恰恰相反。将所有的befor_request——>after_request走完,再从after_request——>befor_request全部执行完。
Flask之befor_request和after_request源码分析
&emsp:由上边代码可知,他是一个befor_request是一个装饰器。
我们先来看一下before_request做了什么
@setupmethod
def before_request(self, f):
# setdefault默认是一个列表,将一个函数添加到一饿列表里,也就是把app.before_request装饰器下的函数,append到一个列表里
# 本质上before_request_funcs = self.before_request_funcs = {}是一个字典,setdfault给这个字典设置值,这个key=None,[]可以理解为是一个列表;
# {None:[bf1,bf2]}
self.before_request_funcs.setdefault(None, []).append(f)
return f
我们继续分析after_request
@setupmethod
def after_request(self, f):
# after_request_funcs = self.after_request_funcs = {}
self.after_request_funcs.setdefault(None, []).append(f)
return f
和before_reqeust原理一样。
当请求过来时候还是走app.run()
中的__call__方法;
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`."""
# Flask框架的请求入口
# environ用户请求相关数据
# start_response 用户响应数据
return self.wsgi_app(environ, start_response)
我们看一下wsgi_app
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
ctx.push()
error = None
try:
try:
# 我们需要往里看,所以我们需要看一下full_dispatch_request
'''
def full_dispatch_request(self):
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
# 我们看一下啊preprocess_request,主要看这里
# funcs = self.before_request_funcs.get(None, ())
# 取key=None的,取到所有函数
# 接下来就是循环和执行,函数默认为空,一旦有一个不为空了
# 就会终止
# 这里就是执行所有的before_request
rv = self.preprocess_request()
# before_request全部通过
if rv is None:
# 执行视图函数
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(e)
# 我们看一下finalize_request函数做了什么,不管有没有返回值,都会执行这个地方。
# 我们主要看他里边的这个方法:response = self.process_response(response)做了什么
'''
def process_response(self, response):
ctx = _request_ctx_stack.top
bp = ctx.request.blueprint
funcs = ctx._after_request_functions
# 这里执行了after_request_func函数就是拿到了app.after_request的所有函数{None:[af1,af2]}
if bp is not None and bp in self.after_request_funcs:
funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
#
if None in self.after_request_funcs:
# 这里拿到[af2,af1]就形成了倒叙
funcs = chain(funcs, reversed(self.after_request_funcs[None]))
# 这里就形成了我们看到的打印结果回去时倒叙
for handler in funcs:
response = handler(response)
if not self.session_interface.is_null_session(ctx.session):
self.save_session(ctx.session, response)
return response
'''
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
ctx.auto_pop(error)
请求和响应
http请求
# request.method
# request.args
# request.form
# request.values
# request.cookies
# 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))
- 响应:
# return "字符串"
# return render_template('html模板路径',**{})
# return redirect('/index.html')
响应头+Cookie:
from flask import make_response
# response = make_response("字符串")
# response = make_response(render_template('index.html'))
# response = make_response(redirect('/index.html'))
# response.delete_cookie('key')
# response.set_cookie('key', 'value')
# response.headers['X-Something'] = 'A value'
# return response
模板引擎
1、模板的使用
Flask使用的时Jinja2模板,所以其语法和Django无差别
2、自定义模板方法
Flask中定义模板方法的方式和Bottle相似,创建一个函数并通过参数形式传入render_template:
例如:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>自定义函数</h1>
{{ww()|safe}}
</body>
</html>
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask,render_template
app = Flask(__name__)
def wupeiqi():
return '<h1>Wupeiqi</h1>'
@app.route('/login', methods=['GET', 'POST'])
def login():
return render_template('login.html', ww=wupeiqi)
app.run()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% macro input(name, type='text', value='') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
{{ input('n1') }}
{% include 'tp.html' %}
<h1>asdf{{ v.k1}}</h1>
</body>
</html>
注意:Markup等价django的mark_safe
session详解
除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。
- 设置:session["username"] = "xxx"
- 删除:session.pop("username",None)
from flask import Flask,session,request,redirect,Markup,render_template
app = Flask("xx",)
app.secret_key = "jklfajfjoqenvcouqwova"
@app.route("/index",methods=["GET"])
def index():
# session就是一个特殊的字典;本质上就是继承了dict;
session["k1"] = 1231
session["k2"] = 321
del session["k1"]
return "index"
if __name__ == '__main__':
app.__call__()
app.run()
请求来了之后,会先执行__call__方法。进入__call__方法可以看到有一个 return self.wsgi_app(environ, start_response)
进入wsgi_app;这里边执行了一个berfor_request和view
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
ctx.push()
'''
def push(self):
……
# 这时候self.session就有值了;
# 去用户请求的cookie中获取值,有就放上值,没有就是空的;
# 特殊的字典= from flask import session
# 我们猜测:返回一个继承自dict类的对象,所以
# open_session方法中又返回了一个session_interface.open_session(self,request)
# 当我们查看session_interface这个对象时他就 = SecureCookieSessionInterface()
# 也就是 SecureCookieSessionInterface对象的open_session方法;
'''
def open_session(self, app, request):
# s 是加密规则
s = self.get_signing_serializer(app)
if s is None:
return None
# 去用户cookies中取值
val = request.cookies.get(app.session_cookie_name)
# 没找到,用户第一次来
if not val:
# 这里session_class(是一个特殊的空字典,
# session_class = SecureCookieSession 它的父类一定会有一个dict
return self.session_class()
max_age = total_seconds(app.permanent_session_lifetime)
try:
# 创建特殊的字典,并将原来cookie中存储的值,再次放入内存,共用户使用。
data = s.loads(val, max_age=max_age)
return self.session_class(data)
except BadSignature:
return self.session_class()
'''
self.session = self.app.open_session(self.request)
if self.session is None:
self.session = self.app.make_null_session()
'''
error = None
try:
try:
# 1、before_request
# 2、视图函数,
# 在执行这两个方法之前要执行ctx.push()
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
ctx.auto_pop(error)
其实self.session的值就是从secureCookieSession(dict)中—__init__返回来的。在requestContext封装了很多的东西,其中就有session。咱们导入的session其实就是requestContex中拿到的session。
我们from flask import session
的 实质就是requestContext中拿到session,这只是第一步,请求刚进来我们把session放到这里
未完待续………………