Flask

Posted ccmldl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flask相关的知识,希望对你有一定的参考价值。

  首先,学习flask之前,先了解一下Django和Flask中是怎么渲染字符串的。在Django中使用mark_safe();在Flask中使用Markup();还可以在html中使用管道符{{ add | safe }}。简单介绍一下Flask,它是轻量型的,第三方组件非常丰富,可扩展性比较强,用法和Django差不多。

一、介绍Flask、Django、Tornado框架

Django:重武器,内部包含了非常多的组件(ORM、Form、ModelForm、缓存、Session、中间件、信号等)

Flask:短小精悍,内部没有太多的组件,第三方组件非常丰富。路由比较特殊,基于装饰器来实现,但是究其本质还是通过add_url_rule来实现。

Tornado:异步非阻塞框架(node.js)

bottle:第三方库比较少

web.py:比较老

二、Flask入门

安装

  pip3  install  flask

WSGI

技术分享图片
 1  from werkzeug.wrappers import Request, Response
 2 
 3 @Request.application
 4 def hello(request):
 5     return Response(Hello World!)
 6 
 7 if __name__ == __main__:
 8     from werkzeug.serving import run_simple
 9     run_simple(localhost, 4000, hello)  # 看到run_simple要知道socket就来了
10         
werkzeug示例(flask)
技术分享图片
 from wsgiref.simple_server import make_server

def runserver(environ, start_response):
    start_response(200 OK, [(Content-Type, text/html)])
    return [bytes(<h1>Hello, web!</h1>, encoding=utf-8), ]
            
if __name__ == __main__:
    # obj = WSGIHandler()
    # httpd = make_server(‘‘, 8000, obj)
    httpd = make_server(‘‘, 8000, runserver)
    print("Serving HTTP on port 8000...")
    httpd.serve_forever()
wsgiref示例(wsgi)
技术分享图片
 1  import socket
 2   
 3 def handle_request(client):
 4     buf = client.recv(1024)
 5     client.send("HTTP/1.1 200 OK

")
 6     client.send("Hello, Seven")
 7               
 8 def main():
 9     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
10     sock.bind((localhost,8000))
11     sock.listen(5)
12               
13     while True:
14         connection, address = sock.accept()
15         handle_request(connection)
16         connection.close()
17               
18 if __name__ == __main__:
19     main()        
本质的本质

flask

技术分享图片
 1 from flask import Flask
 2 
 3 # 实例化Flask对象
 4 app = Flask(__name__)
 5         
 6 @app.route(/)  #  -->1.v = app.route(‘/‘)  2.v(hello_world)
 7 def hello_world():
 8     return Hello World!
 9         
10 if __name__ == __main__:
11     app.run()
View Code

三、配置文件

技术分享图片
 1 class Config(object):
 2     DEBUG = False
 3     TESTING = False
 4     DATABASE_URI = sqlite://:memory:
 5 
 6 class ProductionConfig(Config):
 7     DATABASE_URI = mysql://[email protected]/foo
 8 
 9 class DevelopmentConfig(Config):
10     DEBUG = True
11 
12 class TestingConfig(Config):
13     TESTING = True
14             
15 # 在create_app函数中写上下面这句话,就可以使用配置了
16 app.config.from_object("settings.py.DevelopmentConfig")
settings.py

四、路由系统 

设置URL路由使用route()装饰器,route()装饰哪个函数,那么route()中的参数就映射到哪个函数

路由路径支持变量

  形式:<converter:variable_name>

  converter支持的类型:

    技术分享图片 

  技术分享图片
1 @app.route(/user/<username>)
2 @app.route(/post/<int:post_id>)
3 @app.route(/post/<float:post_id>)
4 @app.route(/post/<path:path>)
5 @app.route(/login, methods=[GET, POST])
示例

@app.route和@app.add_url_rule参数

技术分享图片
1 rule                                         URL规则 
2 view_func                                视图函数名称
3 defaults=None                         默认值,当URL中无参数,函数需要参数时,使用defaults={k:v}为函数提供参数
4 endpoint=None,                       名称,用于反向生成URL,即: url_for(名称)
5 methods=None,                          允许的请求方式,如:["GET","POST"]
6 strict_slashes=False/True              对URL最后的 / 符号是否严格要求
7 注意:如果设置为True,URL后面没有加"/",访问的时候一定不能加"/"
8 redirect_to=None                          重定向到指定地址
9 subdomain=None                          子域名访问
View Code

五、模板语言

Flask使用的是jinja2模板,所以其语法和Django无差别

Flask中自定义模板方法的方式和Bottle相似,创建一个函数并通过参数的形式传入render_template

六、请求&响应相关

技术分享图片
 1 request.method
 2 request.args
 3 request.form
 4 request.values
 5 request.cookies
 6 request.headers
 7 request.path
 8 request.full_path
 9 request.script_root
10 request.url
11 request.base_url
12 request.url_root
13 request.host_url
14 request.host
15 request.files
16 obj = request.files[the_file_name]
17 obj.save(/var/www/uploads/ + secure_filename(f.filename))
请求相关信息
技术分享图片
 1 return "字符串"
 2 return render_template(html模板路径,**{})
 3 return redirect(/index.html)
 4 
 5 response = make_response(render_template(index.html))
 6 # response是flask.wrappers.Response类型
 7 response.delete_cookie(key)
 8 response.set_cookie(key, value)
 9 response.headers[X-Something] = A value
10 return response
响应相关信息

七、Session&Cookie

   除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。
  设置:session[‘username‘] = ‘xxx‘
  删除:session.pop(‘username‘, None)
八、闪现(基于session实现的)
应用:对临时数据操作,如显示错误信息
技术分享图片
 1 from flask import Flask, flash, get_flashed_messages
 2 
 3 app = Flask(__name__)
 4 app.secret_key = asdfasdf
 5 
 6 @app.route(/get)
 7 def get():
 8     # 从某个地方获取设置过得所有值,并清除
 9     data = get_flashed_messages()
10     print(data)
11     return Hello World!
12 
13 @app.route(/set)
14 def set():
15     # 向某个地方设置一个值
16     flash(阿斯蒂芬)
17     return Hello World!
18 if __name__ == __main__:
19     app.run()
代码示例

九、蓝图(blueprint)

目标:构造程序目录
优点:可以批量URL
   指定模板路径/静态文件路径
   请求扩展(针对pp或者针对某个蓝图)
注意:蓝图对象名称和函数名称不能重复
十、请求扩展(Django中间件)
基于before_request做用户登录认证
技术分享图片
1 @app.before_request
2 def process_request(*args, **kwargs):
3     if request.path == /login:
4         return None
5     user = session.get(user_info)
6     if user:
7         return None
8     return redirect(/login)        
代码示例

执行顺序

技术分享图片
 1  from flask import Flask, render_template, request, redirect, session, url_for
 2 app = Flask(__name__)
 3 app.secret_key = asdfasdf
 4 
 5 @app.before_request
 6 def process_request1(*args, **kwargs):
 7     print(process_request1进来了)
 8 
 9 @app.before_request
10 def process_request2(*args, **kwargs):
11     print(process_request2进来了)
12 
13 @app.after_request
14 def process_response1(response):
15     print(process_response1走了)
16     return response
17 
18 @app.after_request
19 def process_response2(response):
20     print(process_response2走了)
21     return response
22 
23 @app.route(/index, methods=[GET])
24 def index():
25     print(index函数)
26     return Index
27 
28 if __name__ == __main__:
29     app.run()
30 
31 运行结果:
32     process_request1进来了
33     process_request2进来了
34     index函数
35     process_response2走了
36     process_response1走了
代码示例

请求拦截后,response所有都执行,函数不再执行

技术分享图片
 1 from flask import Flask, render_template, request, redirect, session, url_for
 2 
 3 app = Flask(__name__)
 4 app.secret_key = asdfasdf
 5 
 6 @app.before_request
 7 def process_request1(*args, **kwargs):
 8     print(process_request1进来了)
 9     return 拦截
10 
11 @app.before_request
12 def process_request2(*args, **kwargs):
13     print(process_request2进来了)
14 
15 @app.after_request
16 def process_response1(response):
17     print(process_response1走了)
18     return response
19 
20 @app.after_request
21 def process_response2(response):
22     print(process_response2走了)
23     return response
24 
25 @app.route(/index, methods=[GET])
26 def index():
27     print(index函数)
28     return Index
29 
30 if __name__ == __main__:
31     app.run()
代码示例

定制错误信息

技术分享图片
1 @app.errorhandler(404)
2 def error_404(arg):
3     return 404错误了
代码示例

模板中定义方法

技术分享图片
 1 @app.template_global()
 2 def sb(a1, a2):
 3     return a1 + a2
 4 
 5 # 在HTNL中使用:{{sb(1,2)}}
 6         
 7 @app.template_filter()
 8 def db(a1, a2, a3):
 9     return a1 + a2 + a3
10 
11 # 在HTNL中使用:{{ 1|db(2,3)}}
代码示例

第一次进来要执行的操作

技术分享图片
1 @app.before_first_request
2 def first(*args, **kwargs):
3     pass
代码示例

十一、中间件

技术分享图片
 1 from flask import Flask, request
 2 
 3 app = Flask(__name__)
 4 
 5 @app.route(/)
 6 def index():
 7     return Hello World!
 8 
 9 
10 class Md(object):
11     def __init__(self, old_wsgi_app):
12         self.old_wsgi_app = old_wsgi_app
13     def __call__(self, environ, start_response):
14         print(开始之前)
15         ret = self.old_wsgi_app(environ, start_response)
16         print(结束之后)
17         return ret
18 
19 if __name__ == __main__:
20     app.wsgi_app = Md(app.wsgi_app)
21     app.run()
代码示例

十二、上下文管理

请求上下文
threading.local对象,用于为每个线程开辟一块空间来保存它独有的值
技术分享图片
 1 import threading
 2 import time
 3 
 4 local_values = threading.local()
 5 
 6 class Foo(object):
 7     def __init__(self):
 8         self.name = 0
 9 
10 local_values = Foo()
11 
12 def func(num):
13      local_values.name = num
14      time.sleep(1)
15      print(local_values.name, threading.current_thread().name)
16 
17 for i in range(20):
18     th = threading.Thread(target=func, args=(i,), name=线程%s % i)
19     th.start()
代码示例

源码(request)

  情况一:单进程单线程,基于全局变量做
  情况二:单进程多进程,基于threading.local对象做
  情况三:单进程单线程(多个协程),threading.local对象做不到
决定
  以后不支持协程:threading.local对象
  支持:自定义threading.local对象(支持协程)
  技术分享图片
 1  import threading
 2 
 3 try:
 4     from greenlet import getcurrent as get_ident  # 协程
 5 except ImportError:
 6     try:
 7         from thread import get_ident
 8     except ImportError:
 9          from _thread import get_ident  # 线程
10 
11 
12 class local(object):
13     def __init__(self):
14         self.storage = {}
15         self.get_ident = get_ident
16     
17     def set(self, k, v):
18          ident = self.get_ident()
19          origin = self.storage.get(ident)
20          if not origin:
21             origin = {k:v}
22          else:
23             origin[k] = v
24           self.storage[ident] = origin
25      
26     def get(self, k):
27            ident = self.get_ident()
28            origin = self.storage.get(ident)
29            if not origin:
30                return None
31            return origin.get(k, None)
32 
33 local_values = local()
34 
35 def task(num):
36     local_values.set(name, num)
37     import time
38     time.sleep(1)
39   print(local_values.get(name),threading.current_thread().name)
40 
41 for i in range(20):
42     th = threading.Thread(target=task, args=(i,), name=线程%s % i)
43     th.start()    
代码示例

自定义类似threading.local对象

  一、self.storage={}
    object.__setattr__(self, ‘storage‘, {})  使用此方法时,对象.xx不会调用__setattr__()方法
  二、对象.xx
    触发:
      def __setattr__(self, key, value):
        print(key, value)
flask上下文管理机制
  1.threading.local对每个线程保留它自己的值,但是flask为了支持协程,它自己定义了一个Local对象,其中创建了一个字典{greenlet做唯一标识:存数据},保证数据隔离,在Local对象中保存它的值
  2.三个流程
    a.请求到来时
      ctx=封装RequestContext(request, session)
      ctx放到local中
      请求刚进来封装了一个RequestContext对象,ctx=封装RequestContext(request,session),ctx通过push()加到local对象中
      具体用到的是_local.stack把信息加到local对象中
    b.执行视图时
      导入request
      print(request)        --> LocalProxy对象的__str__方法
      request.method    --> LocalProxy对象的__getattr__方法
      request + 1           --> LocalProxy对象的__add__方法
      都会调用_lookup_req_object函数:去local中将requestContext获取到,再去requestContext中获取request或session
    c.请求结束
      ctx.auto_pop()
      ctx从local中移除
      拿到之后在执行_request_ctx_stack.pop(),去local里面把原来请求相关的requestContext对象删除掉,这次请求终止
  3.与Django相比是两种不同的实现方式
    Django/tornado是通过传参数形式
    flask是通过上下文管理,两种方式都可以实现,只不过实现方式不一样
应用上下文
请求流程:
  _request_ctx_stack.local = { }
        _app_ctx_stack.local = { }
  1.请求到来,有人来访问
    技术分享图片
 1 # ctx = RequestContext对象
 2 # 将请求相关的数据environ封装到了RequestContext对象中
 3 # 再将对象封装到Local中(每个线程/协程独立空间存储)
 4 # ctx.app(当前app的名称)
 5 # ctx.request(封装请求相关的东西)
 6 # ctx.session 空
 7 _request_ctx_stack.local = {
 8     唯一标识:{
 9         stack:[ctx,]
10     },
11     唯一标识:{
12          stack:[ctx,]
13     },
14 }
15 # app_ctx = AppContext对象
16 # app_ctx.app
17 # app_ctx.g
18 _app_ctx_stack.local = {
19     唯一标识:{
20         stack:[app_ctx,]
21     },
22     唯一标识:{
23          stack:[app_ctx,]
24     },
25 }
View Code

  2.使用

    技术分享图片
1 from flask import request, session, g, current_app
2 print(request, session, g, current_app)
3 都会执行相应LocalProxy对象的__str__
4 request = LocalProxy(partial(_lookup_req_object, request))
5 session = LocalProxy(partial(_lookup_req_object, session))
6 current_app = LocalProxy(_find_app)
7 g = LocalProxy(partial(_lookup_app_object, g))
View Code

  3.终止,ctx、app_ctx全部pop

问题1:多线程是如何实现?

  不管几个线程进来都是两个local对象,只不过是每个线程的唯一标识不同,而所有线程的唯一标识都放在对应的Local对象中,使用时取自己对应的不会出错

问题2:flask的local中保存数据时,使用列表创建出来的是栈

  如果写web程序,web运行环境:栈中永远保存1条数据(可以不用栈)

  写脚本获取app信息时,可能存在app上下文嵌套关系(要使用栈)

    技术分享图片
 1 from flask import Flask, current_app, globals, _app_ctx_stack
 2 app1 = Flask(app01)
 3 app1.debug = True  
 4 app2 = Flask(app02)
 5 app2.debug = False
 6 with app1.app_context():
 7     print(current_app.name)
 8     print(_app_ctx_stack._local.__storage__)
 9 with app2.app_context():
10     print(current_app.name)
11     print(_app_ctx_stack._local.__storage__)
12 print(current_app.name)
代码示例

多app应用

技术分享图片
 1 from werkzeug.wsgi import DispatcherMiddleware
 2 from werkzeug.serving import run_simple
 3 from flask import Flask, current_app
 4 app1 = Flask(app01)
 5 app2 = Flask(app02)
 6 @app1.route(/index)
 7 def index():
 8     print(current_app)
 9     return "app01"
10 @app2.route(/index2)
11 def index2():
12     print(current_app)
13     return "app02"
14 
15 # http://www.oldboyedu.com/index
16 # http://www.oldboyedu.com/sec/index2
17 # 通过URL分发
18 dm = DispatcherMiddleware(app1, {
19             /sec: app2,
20 })
21 
22 if __name__ == __main__:
23     # app2.__call__
24     run_simple(localhost, 5000, dm) 
代码示例

问题3:web访问多app应用时,上下文管理是如何实现?

  每个app都会调用自己的__call__方法,而且都有自己的唯一标识,并且添加到相应的local对象中,只是对应的app是不一样的,执行过程和多线程实现过程类似

补充:当用脚本写flask时,有可能会出现堆栈

技术分享图片
 1 from flask import Flask, current_app, globals, _app_ctx_stack
 2 app1 = Flask(app01)
 3 app1.debug = True   # 用户名/密码/邮箱
 4 app2 = Flask(app02)
 5 app2.debug = False  
 6 # with AppContext(self):   # 执行__enter__方法
 7 # app_ctx = AppContext(self)
 8 # app_ctx.app
 9 # app_ctx.g
10 with app1.app_context():# 执行__enter__方法 -> push -> app_ctx添加到_app_ctx_stack.local
11 # {<greenlet.greenlet object at 0x00000184FEEBCCC0>: {‘stack‘: [<flask.ctx.AppContext object at 0x00000184FEFC5748>]}}
12 print(current_app.name)  # app01
13 # print(current_app.config[‘DEBUG‘])
14 print(_app_ctx_stack._local.__storage__)
15 with app2.app_context():
16 # {<greenlet.greenlet object at 0x00000184FEEBCCC0>: {‘stack‘: [<flask.ctx.AppContext object at 0x00000184FEFC5748>, <flask.ctx.AppContext object at 0x00000184FEFC5860>]}}
17 print(current_app.name)  # app02
18 # print(current_app.config[‘DEBUG‘])
19 print(_app_ctx_stack._local.__storage__)
20 print(current_app.name)  # app01
View Code
十三、偏函数
技术分享图片
1 import functools
2 
3 def func(a1, a2):
4     print(a1, a2)
5 
6 new_func = functools.partial(func, 666)
7 new_func(999)
代码示例

十四、面向对象

当把面向对象中的所有__函数__实现时,对象做任何操作时,都会执行其中对应的方法
技术分享图片
 1 class Foo(object):
 2     def __init__(self, num):
 3         self.num = num
 4     
 5     def __add__(self, other):
 6         data = self.num + other.num
 7         return Foo(data)
 8 
 9 obj1 = Foo(1)
10 obj2 = Foo(2)
11 v = obj1 + obj2
代码示例

面向对象私有

技术分享图片
 1 class Foo(object):
 2     def __init__(self):
 3         self.name = alex
 4         self.__age = 18
 5     
 6     def get_age(self):
 7          return self.__age
 8 
 9 obj = Foo()
10 print(obj.name)
11 # 强制获取私有字段
12 print(obj._Foo__age)
13 print(obj.get_age())
代码示例

十五、拼接列表中的值

技术分享图片
1 from itertools import chain
2 
3 v1 = [11, 22, 33]
4 v2 = [44, 55, 66]
5 new = chain(v1, v2)
6 for item in new:
7     print(item)
实例一
技术分享图片
 1 from itertools import chain
 2 
 3 def f1(x):
 4     return x + 1
 5 
 6 func1_list = [f1, lambda x:x-1]
 7 
 8 def f2(x):
 9     return x + 10
10 
11 new_fun_list = chain([f2], func1_list)
12 
13 for func in new_fun_list:
14     print(func)
实例二

十六、数据库连接池(基于threading.local实现)

友情链接:https://www.cnblogs.com/wupeiqi/articles/8184686.html

Django和Flask使用数据库分析

  Django:ORM(pymysql/MySQLdb)
  Flask:原生SQL
        pymysql(2/3)
        MySQLdb(2)
      SQLAlchemy(ORM)
原生SQL
技术分享图片
 1 import pymysql
 2 CONN = pymysql.connect(host=127.0.0.1,
 3                                        port=3306,
 4                                        user=root,
 5                                        password=123,
 6                                        database=pooldb,
 7                                        charset=utf8)
 8 cursor = CONN.cursor()
 9 cursor.execute(select * from tb1)
10 result = cursor.fetchall()
11 cursor.close()
12 print(result)
代码示例

问题

  1.来一个用户连接一次数据库(把连接数据库的操作放到全局变量中)

  2.并发运行时,拿到的数据有可能是错的

  3.加锁可以解决信息错误的问题,但是没有并发运行

解决思路

  不能为每个用户创建一个连接

  创建一定数量的连接池,如果有人来使用时有空的就拿去用,用完再还回来,没有时就等待

使用DBUtils模块

  安装:如果安装到虚拟环境,需要先切换到虚拟环境
  使用:
    模式一:为每个线程创建一个连接
    模式二:创建n个连接,多线程来时,去获取
  技术分享图片
 1 import time
 2 import pymysql
 3 from DBUtils.PooledDB import PooledDB
 4 
 5 POOL = PooledDB(
 6     creator=pymysql,  # 使用链接数据库的模块
 7     maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
 8     mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
 9     maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
10     maxshared=3,  # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
11     blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
12     maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
13     setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
14     ping=0,
15     # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
16     host=127.0.0.1,
17     port=3306,
18     user=root,
19     password=123,
20     database=pooldb,
21     charset=utf8
22 )
23 
24 class SQLHelper(object):
25 @staticmethod
26 def fetch_one(sql, args):
27     conn = POOL.connection()
28     cursor = conn.cursor()
29     cursor.execute(sql, args)
30     result = cursor.fetchone()
31     conn.close()
32     return result
33     
34 @staticmethod
35 def fetch_all(sql, args):
36     conn = POOL.connection()
37     cursor = conn.cursor()
38     cursor.execute(sql, args)
39     result = cursor.fetchall()
40     conn.close()
41     return result
42 
43 result = SQLHelper.fetch_one(select * from tb1 where id > %s, [1,])
44 print(result)
代码示例
十七、信号(只能执行不能终止)
Django:要有请求、有信号、信号中注册函数
Flask:
  before_first_request
  触发request_started信号
  before_request
  模板渲染
    渲染前的信号:before_render_template.send(app, template=template, context=context)
      rv = template.render(context)  # 模板渲染
    渲染后的信号:template_rendered.send(app, template=template, context=context)
  after_request
  session.save_session()
  触发request_finished信号
  如果上述过程出错:
    触发错误处理信号:got_request_exception.send(self, exception=e)
  触发信号:request_tearing_down
十八、MetaClass
作用:用来指定当前类是由谁来创建(默认type创建)
类由type创建:class Foo(metaclass=type)
继承type:class Foo(type)
十九、Flask-session
Flask中的session处理机制(内置:将session保存在加密的cookie中实现)
  请求刚进来:获取随机字符串,存在则去"数据库"中获取原来的个人数据,否则创建一个空容器。--> 内存:对象(随机字符串,{放置数据的容器})
    # 到底是什么对象?
              # 1.obj = 创建SecureCookieSessionInterface()
              # 2.obj = open_session(self.app, self.request) = SecureCookieSession()
              # self.session = SecureCookieSession()对象
              # 为session创建一个特殊的字典
              self.session = session_interface.open_session(self.app, self.request)
  视图:操作内存中对象(随机字符串,{放置数据的容器})
    响应:内存对象(随机字符串,{放置数据的容器})
      将数据保存到数据库
      把随机字符串写在用户cookie中            
  自定义
    请求刚进来:
      # 创建特殊的字典,并添加到Local中
                   # 调用关系:
                     #     self.session_interface.open_session(self, request)
                     #     由于默认app中的session_interface = SecureCookieSessionInterface()
                     #         SecureCookieSessionInterface().open_session(self, request)
                     #   由于默认app中的session_interface = MySessionInterFace()
                     #         MySessionInterFace().open_session(self, request)
                     self.session = session_interface.open_session(self.app, self.request)
    调用:
      session --> LocalProxy --> 偏函数 --> LocalStack --> Local
    请求终止:
      #     由于默认app中的session_interface = SecureCookieSessionInterface()
                     #         SecureCookieSessionInterface().save_session(self, app, session, response)
                     #   由于默认app中的session_interface = MySessionInterFace()
                     #         MySessionInterFace().save_session(self, app, session, response)
  flask-session组件
    随机生成一个ID
      >>> from uuid import uuid4
                     >>> uuid4()
                  UUID(‘81a3ae5a-991f-4eb9-9e1d-76c11d248887‘)
    使用:  
      技术分享图片
 1  from flask import Flask, session
 2 from flask_session import RedisSessionInterface
 3 app = Flask(__name__)
 4 app.secret_key = asdfasdf
 5 
 6 # 方式一
 7 from redis import Redis
 8 conn = Redis()
 9 app.session_interface = RedisSessionInterface(conn, key_prefix=__, use_signer=False)
10 
11 # 方式二
12 from redis import Redis
13 from flask_session import Session
14 app.config[SESSION_TYPE] = redis
15 app.config[SESSION_REDIS] = Redis(host=localhost,port=6379)
16 Session(app)
17 
18 @app.route(/)
19 def index():
20     session[xxx] = 123
21     return "index"
22 
23 if __name__ == __main__:
24     app.run()
代码示例
    问题:设置cookie时,如何设定关闭浏览器则cookie失效
      request.set_cookie(‘k‘, ‘v‘, exipre=None)   
二十、总结
1.哪里还用到过threading.local
  DBUtils
2.上下文管理
  请求:
    request:封装请求相关信息
    session:保存用户回话相关信息
  应用:
    app:当前应用相关信息
    g:每个请求周期都会创建一个用于在请求周期中传递值的一个容器
3.多app应用&蓝图
  都是分发URL,多app应用是在app前面分发,蓝图是在app后面分发
4.为什么用栈?
  存在app上下文嵌套关系时,会将信息堆栈,但是不用担心取值的问题,因为当前app每次取值都是取的最后一个,用完之后就清除了,而不会影响其他app的使用,相当于一个先进后出的队列
5.面向对象
  封装 
    技术分享图片
 1  class Foo:
 2     def __init__(self):
 3        self.age = 23
 4        self.name = xiaoming
 5 
 6 class Bar:
 7     def __init__(self):
 8         self.xx = 123
 9 
10 # 把类再封装到一个对象里面
11 class Base:
12     def __init__(self):
13         self.f = Foo()
14         self.x = Bar()
View Code

  某个值 + 括号

    函数/方法:看谁调用,判断函数或方法

    类和对象

  特殊的双下划线方法,flask中的LocalProxy里面都使用过

    技术分享图片
 __new__
__init__
__call__
__str__
__setattr__
__getattr__
__setitem__
__enter__
__exit__
__add__
View Code

  强制调用私有字段,只能在自己这个类中调用

    子类和派生类中都不能调用基类私有字段



以上是关于Flask的主要内容,如果未能解决你的问题,请参考以下文章

12_关于flask中的宏

Flask之模板之宏继承包含

Flask模板宏的概念和基本使用

python flask(多对多表查询)

python后端 flask框架 计算时间差 并根据时间差条件返回flag值

python后端 flask框架 计算时间差 并根据时间差条件返回flag值