03-Python 框架 Flask

Flask

Flask 框架的优势

Flask 自由、灵活、可扩展性强、透明可控、第三方库的选择面广、开发时可以结合最流行最强大的 Python 库。

Flask 是单线程还是多线程 ★★★

Flask 默认是单进程单线程的。

不过可以配置 Flask 为多线程或者多进程。多线程或者多进程只能二选一。

  • 多进程:当请求进来时,fork() 一个子进程执行。
  • 多线程:当请求进来时,为每一个请求分配一个线程执行。

简述 Flask 上下文管理流程 ★★★

简单来说,Falsk 上下文管理可以分为三个阶段:

  1. 请求进来时:将请求相关的数据 request、session、app、g 放入上下文管理中。
  2. 在视图函数中:要去上下文管理中取值。
  3. 请求响应:要将上下文管理中的数据清除。

详细点来说:

  1. werkzeug 处理:
    werkzeug 先把请求进行封装,然后转到 Flask 进行处理。

  2. 请求刚进来:
    将 request、session 封装在 RequestContext 类中,app、g 封装在 AppContext 类中。
    并通过 LocalStack 将 RequestContext 和 AppContext 放入 Local 类中。

  3. 视图函数中:
    通过 localproxy —> 偏函数 —> localstack —> local 取值。

  4. 请求响应时:
    先执行 save.session(),再各自执行 pop(),将 local 中的数据清除。

Flask blueprint(蓝图)的作用

Flask blueprint 把实现不同功能的 module 分开。也就是把一个大的 App 分割成各自实现不同功能的 module。

在一个 blueprint 中可以调用另一个 blueprint 的视图函数, 但要加相应的 blueprint 名。

列举使用的 Flask 第三方组件

  • Flask 组件
    • flask-session session 放在 Redis
    • flask-SQLAlchemy 同 Django 的 ORM 操作
    • flask-migrate 数据库迁移
    • flask-script 自定义命令
    • blinker 信号-触发信号
  • 第三方组件
    • Wtforms 快速创建前端标签、文本校验,和 Django 的 ModelForm 作用类似。
    • dbutile 创建数据库连接池
    • gevnet-websocket 实现 websocket
  • 自定义 Flask 组件
    • 自定义 auth 认证,参考 flask-login 组件

Flask 框架依赖组件

  • Jinja2 模板。
  • werkzurg。

在 Flask 中实现 WebSocket 需要什么组件?

gevent-websocket

Flask 中上下文管理主要涉及到了那些相关的类?并描述类主要作用?

  • RequestContext # 封装进来的请求(赋值给ctx)
  • AppContext # 封装 app_ctx
  • LocalStack # 将 local 对象中的数据维护成一个栈(先进后出)
  • Local # 保存请求上下文对象和app上下文对象

Flask 默认 session 处理机制

不熟的话:记不太清了,应该是……分两个阶段吧。

  • 创建:
    当请求刚进来的时候,会将 requestsession 封装成一个 RequestContext() 对象。
    接下来把这个对象通过 LocalStack() 放入内部的一个 Local() 对象中。
    因为刚开始 Local 的 ctx 中 session 是空的,所以接着执行 open_session,将 cookie 里面的值拿过来,重新赋值到 ctx 中(Local实现对数据隔离,类似 threading.local)

  • 销毁:
    最后返回时执行 save_session() 将 ctx 中的 session 读出来进行序列化,写到 cookie 然后给用户,接着把 ctx pop 掉。

Flask 中的 g 的作用

g 是贯穿于一次请求的全局变量,当请求进来将 gcurrent_app 封装为一个 APPContext 类,再通过 LocalStack 将 Appcontext 放入 Local 中。

取值时通过偏函数在 LocalStack、local 中取值;响应时将 local 中的 g 删除。

在 Flask 中,g 对象是专门用来存储用户数据的,它是 global 的缩写,g 是全局变量,在整个 request 生命周期内生效。

g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的 thread id 区别,像数据库配置这样重要的信息挂载在 app 对象上,一些用户相关的数据,就可以挂载在 g 对象上,这样就不需要在函数里一层层传递。

为什么 Flask 把 Local 对象中的的值 stack 维护成一个列表

因为通过维护成列表,可以实现一个栈的数据结构,进栈出栈时只取一个数据,巧妙的简化了问题。

还有,在多 app 应用时,可以实现数据隔离;列表里不会加数据,而是会生成一个新的列表。

local 是一个字典,字典里key(stack)是唯一标识,value是一个列表。

Flask 中多 app 应用是怎么完成

请求进来时,可以根据 URL 的不同,交给不同的 APP 处理。蓝图也可以实现。

1
2
3
4
#app1 = Flask('app01')
#app2 = Flask('app02')
#@app1.route('/index')
#@app2.route('/index2')

源码中在 DispatcherMiddleware 类里调用 app2.__call__,原理其实就是 URL 分割,然后将请求分发给指定的 app。之后 app 也按单 app 的流程走。就是从 app.__call__ 走。

解释 Flask 框架中的 Local 对象和 threading.local 对象的区别

  • Local 对象:Local 对象是根据 threading.local 做的,为每个 request 开辟一块空间进行数据存储。

  • threading.local:为每个线程开辟一块空间进行数据存储(数据隔离)。

问题:自己通过字典创建一个类似于 threading.local 的东西。

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
storage = {
4740: {val: 0},
4732: {val: 1},
4731: {val: 3},
}


class Local(object):
def __init__(self):
object.__setattr__(self, 'storage', {})
def __setattr__(self, k, v):
ident = get_ident()
if ident in self.storage:
self.storage[ident][k] = v
else:
self.storage[ident] = {k: v}
def __getattr__(self, k):
ident = get_ident()
return self.storage[ident][k]
obj = Local()
def task(arg):
obj.val = arg
obj.xxx = arg
print(obj.val)
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()

Flask 中 blinker 是什么?

Flask 中的 blinker 是信号的意思,信号主要是让开发者可是在 Flask 请求过程中定制一些行为。

或者说 Flask 在列表里面预留了几个空列表,在里面存东西。简言之,信号允许某个’发送者’通知’接收者’有事情发生了。

@before_request 有返回值,blinker 没有返回值

1
2
3
4
5
6
7
8
9
10
11
# 10个信号
request_started = _signals.signal('request-started') # 请求到来前执行
request_finished = _signals.signal('request-finished') # 请求结束后执行
before_render_template = _signals.signal('before-render-template') # 模板渲染前执行
template_rendered = _signals.signal('template-rendered') # 模板渲染后执行
got_request_exception = _signals.signal('got-request-exception') # 请求执行出现异常时执行
request_tearing_down = _signals.signal('request-tearing-down') # 请求执行完毕后自动执行(无论成功与否)
appcontext_tearing_down = _signals.signal('appcontext-tearing-down')# 请求上下文执行完毕后自动执行(无论成功与否)
appcontext_pushed = _signals.signal('appcontext-pushed') # 请求app上下文push时执行
appcontext_popped = _signals.signal('appcontext-popped') # 请求上下文pop时执行
message_flashed = _signals.signal('message-flashed') # 调用flask在其中添加数据时,自动触发

SQLAlchemy 中的 sessionscoped_session 的区别?

  • Session:
    由于无法提供线程共享功能,开发时要给每个线程都创建自己的 session,打印 sesion 可知他是 sqlalchemy.orm.session.Session 的对象。

  • scoped_session:
    为每个线程都创建一个 session,实现支持线程安全,在整个程序运行的过程当中,只存在唯一的一个 session 对象。
    创建方式:通过本地线程 Threading.Local() session=scoped_session(Session) 创建唯一标识的方法(参考flask请求源码)。

SQLAlchemy 如何执行原生SQL?

1
2
3
4
5
# 使用execute方法直接操作SQL语句(导入create_engin、sessionmaker)
engine=create_engine('mysql://root:*****@127.0.0.1/database?charset=utf8')
DB_Session = sessionmaker(bind=engine)
session = DB_Session()
session.execute('alter table mytablename drop column mycolumn ;')

ORM 的实现原理?

ORM 的实现基于一下三点:

  • 映射类:描述数据库表结构。
  • 映射文件:指定数据库表和映射类之间的关系。
  • 数据库配置文件:指定与数据库连接时需要的连接信息(数据库、登录用户名、密码or连接字符串)。

以下 SQLAlchemy 的字段是否正确?如果不正确请更正:

1
2
3
4
5
6
7
8
9
10
11
12
13
from datetime import datetime
from sqlalchemy.ext.declarative
import declarative_base
from sqlalchemy import Column, Integer, String, DateTime

Base = declarative_base()
class UserInfo(Base):
__tablename__ = 'userinfo'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(64), unique=True)
ctime = Column(DateTime, default=datetime.now())

# 不正确:Ctime字段中参数应为 default=datetime.now,now 后面不应该加括号,加了的话,字段不会实时更新。

SQLAchemy 中如何为表设置引擎和字符编码?

  1. 设置引擎编码方式为utf8。
    engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/sqldb01?charset=utf8")

  2. 设置数据库表编码方式为utf8

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class UserType(Base):
    __tablename__ = 'usertype'
    id = Column(Integer, primary_key=True)
    caption = Column(String(50), default='管理员')

    # 添加配置设置编码
    __table_args__ = {
    'mysql_charset':'utf8'
    }

    这样生成的SQL语句就自动设置数据表编码为utf8了,__table_args__ 还可设置存储引擎、外键约束等等信息。

SQLAchemy 中如何设置联合唯一索引

通过 UniqueConstraint 字段来设置联合唯一索引,例如 __table_args=(UniqueConstraint('h_id','username',name='_h_username_uc')) h_id 和 username 组成联合唯一约束。


03-Python 框架 Flask
https://flepeng.github.io/interview-20-开发语言类-21-Python-03-Python-框架-Flask/
作者
Lepeng
发布于
2020年8月8日
许可协议