Flask基础(二)

First Post:

Last Update:

列表提取与元组提取注意点:

如果是底层配置需求(有错需要警告的话)就用方法[‘’]提取,空就会报错
如果是无关大雅的外层需求,可以用get.(‘’)来提取,空也不会报错

蓝图(注意避免循环导包)

1
2
3
4
5
6
7
8
9
10
11
from flask import Blueprint, url_for
# 1.创建蓝图对象
home_bp = Blueprint("hone", __name__, url_prefix = '/home')

# 在蓝图中使用钩子函数[作用域:整个home模块]

@home_bp.before_request
def process_request():
print ("before_request调用了")

from .views import *

上下文(数据容器)

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
1.请求上下文:request, session
def hello_world():
# =======[请求上下文]==========
print(request.url)

session["username"] = "xiaoming"
print(session.get("username"))

2.应用上下文:current_app

# ========[应用上下文]============
# current_app == app 当前运行的app
# 别的模块不方便导入app,够不着app的时候可以current_app
print(app.config.get("JSON_AS_ASCII"))
print(app.config.get("SECRET_KEY"))

print(current_app.config.get("JSON_AS_ASCII"))
print(current_app.config.get("SECRET_KEY"))
理解:1.上下文不是全局变量,web是并发调用,全局变量是线程共享
2.上下文是线程隔离(LocalStack类型, 本质是字典)

# 问题
在Flask程序未运行的情况下,调试代码时需要使用current_app、g、request这些对象,会不会有问题?该如何使用?

# 解决方案
手动开启应用上下文 【重点】
with app.app_context():
current_app

手动开启请求上下文
with app.request_context(environ):


# 模拟解析客户端请求之后的wsgi字典数据
environ = {'wsgi.version':(1,0), 'wsgi.input': '', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/', 'SERVER_NAME': 'itcast server', 'wsgi.url_scheme': 'http', 'SERVER_PORT': '80'}


g对象与请求钩子的综合案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 需求:构建用户身份认证机制
# 1.每次进入视图函数之前对用户身份进行认证(jwt session cookie),
将用户信息保存到g对象中,方便在别的函数使用用户信息
# 2.对于特定视图可以提供强制要求用户登录的限制

# 分析
每次进入视图函数之前对用户身份进行认证,并保存用户信息 --> 钩子函数
提供强制要求用户登录的限制 ---> 装饰器

# 数据如何传递?
g对象

# 执行流程:
发送请求 --> 钩子函数(登录使用g变量保存用户信息,没有登录g变量保存None) --> 视图函数
发送请求 --> 钩子函数 --> 装饰器 --> 视图函数 --> 获取用户信息

g对象与请求钩子

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
from flask import Flask, session, g, request
import functools

app = Flask(__name__)
app.secret_key = "alksdjalkdjlkas***"


# 需求:限制视图函数的访问权限
# eg: 个人中心,评论,点赞接口必须先登录,才能访问视图函数
# 方案:在钩子函数中统一提取用户信息,并且使用g变量存储,再将用户是否登录的业务逻辑代码封装到装饰器中[减少代码冗余]
@app.before_request
def get_userinfo():
# 注意:必须用get获取字典的键值对信息
g.user_id = session.get("user_id")
g.user_name = session.get("user_name")

# TODO:
# token = request.headers.get("token")
# token校验 -- payload -- 用户信息 -- 登录装饰器判断是否登录 -- 视图函数


# 主动寻求装饰
# app.before_request(get_userinfo)


# 登录装饰器
def login_required(view_func):
# 防止装饰器修改被装饰函数名和文档信息
@functools.wraps(view_func)
def wrapper(*args, **kwargs):
# 判断用户是否登录
if g.user_id and g.user_name:
# 进入视图函数
return view_func(*args, **kwargs)
else:
# 未登录
return "invliad user", 401

return wrapper


@app.route('/login')
def login():
# 状态保持
session["user_id"] = 66
session["user_name"] = "james"

return 'login success'


@app.route('/index')
def index():
# 提取用户信息
if g.user_id and g.user_name:
return "欢迎回来:{} ".format(g.user_name)
else:
return '<a href="/login">去登录</a>'


# 注意:装饰顺序 先构建请求,再判断是否有登录
@app.route('/profile')
@login_required
def profile():
# 判断用户是否有登录
print(g.user_name)
return "profile page"


@app.route('/comment')
@login_required
def comment():
# 判断用户是否有登录
print(g.user_name)
return "profile page"


if __name__ == '__main__':
app.run(debug=True, port=8000)

工厂方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 需求:将app创建封装到工厂方法中
def create_app(config_name):
# 1.创建app
app = Flask(__name__)

# 2.读取配置类中的配置信息
config_class = config_dict[config_name]
app.config.from_object(config_class)

# 3.读取环境变量中私有配置信息
# 后加载的同名配置信息会覆盖之前的配置信息
app.config.from_envvar("CONFIG", silent=True)

# 4.返回app
return app


# 开发模式下的app对象
app = create_app("dev")

配置文件

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
# 使用面向对象的方式封装多套配置信息
class BaseConfig(object):
"""
配置类基类
以类属性的方式添加配置信息
"""
DEBUG = True
SECRET_KEY = "python666"


class DevelopmentConfig(BaseConfig):
"""开发阶段配置信息"""
DEBUG = True

# redis 配置信息 【自定义】
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379


class ProductionConfig(BaseConfig):
"""上线配置信息"""
# 减少io 减少服务器压力
DEBUG = False

# redis 配置信息 【自定义】
REDIS_HOST = "192.168.1.3"
REDIS_MASTER_PORT = 6379
REDIS_SLAVE_PORT = 6380


class TestingConfig(BaseConfig):
"""测试配置信息"""
# 提示代码错误
Testing = True

# redis 配置信息
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379


# 暴露外界调用的接口
config_dict = {
"dev": DevelopmentConfig,
"pro": ProductionConfig,
"test": TestingConfig
}


单例设计模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Singleton(object):

# 对象初始化
# def __init__(self):
# pass

# cls == Singleton
def __new__(cls, *args, **kwargs):
# 第一次:cls上没有_instance 进入if判断创建对象
# 以后的每一次:cls上有instance 返回的是第一次创建的对象
if not hasattr(cls, "_instance"):
# 第一次类中没有_instance属性 进入if判断,创建对象,保存_instance属性中
# super(Singleton, cls).__new__(*args, **kwargs) 调用父类的__new__创建对象
# 动态属性赋值
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)

return cls._instance