文章目录
1. 介绍
Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。
“微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。
默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用
wsgiref
最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。
如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。
正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口协议来实现这样的服务器软件,让我们专心用Python编写Web业务。这个接口就是WSGI
:Web Server Gateway Interface。而wsgiref模块就是python基于wsgi协议开发的服务模块
from wsgiref.simple_server import make_server
def mya(environ, start_response):
print(environ)
start_response('200 OK', [('Content-Type', 'text/html')])
if environ.get('PATH_INFO') == '/index':
with open('templates/index.html', 'rb') as f:
data = f.read()
elif environ.get('PATH_INFO') == '/home':
with open('templates/home.html', 'rb') as f:
data = f.read()
else:
data = b'<h1>Hello, web!</h1>'
return [data]
if __name__ == '__main__':
myserver = make_server('', 8010, mya)
print('监听8010')
myserver.serve_forever()
# wsgiref简单应用
2. 初步使用
安装:pip3 install flask
from flask import Flask
# 实例化产生一个Flask对象
app = Flask(__name__)
# 将 '/'和视图函数hello_workd的对应关系添加到路由中
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run() # 最终调用了run_simple()
新手四件套:
返回字符串 return 字符串 django中 HttpResponse('字符串')
返回模板 return render_template django中 render(request,模板,{})
返回重定向 return redirect('/login') django中 redirect('/login')
返回json return jsonify(字典,列表) django中 JsonResponse
3. 配置文件
3.1 配置文件写法
flask 配置文件写法很多种,如下所示:
- 第一种:直接配置
app = Flask(__name__)
app.secret_key = 'amsnjabcascasdnknfv'
app.debug = True
- 第二种:app 所有的配置项都在 app.config 这个字典中
app = Flask(__name__)
app.config['DEBUG'] = True
- 第三种:编写一个 settings.py 文件用于存放配置
# settings.py
NAME = 'xwx'
app = Flask(__name__)
# 通过py文件获取配置
app.config.from_pyfile('settings.py')
print(app.config['NAME'])
- 第四种:通过类的方式(常用)
创建 settings.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
使用
app = Flask(__name__)
app.config.from_object('settings.DevelopmentConfig')
print(app.config)
- 其他方式
# 通过环境变量配置
app.config.from_envvar("环境变量名称")
# app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
# 环境变量的值为python文件名称名称,内部调用from_pyfile方法
app.config.from_json("json文件名称")
# JSON文件名称,必须是 json 格式,因为内部会执行 json.loads
app.config.from_mapping({'DEBUG': True})
# 字典格式
3.2 默认配置
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,
}
4. 路由
django 与 flask 路由的区别:
-
在django中,路由是浏览器访问服务器时,先访问的项目中的url,再由项目中的url找到应用中url, 这些url是放在一个列表里,遵从从前往后匹配的规则。
-
在flask中,路由是通过装饰器给每个视图函数 提供的,而且根据请求方式的不同可以一个url用于不同的作用。
4.1 路由典型写法
@app.route('/detail/<int:nid>', methods=['GET'], endpoint='detail')
@app.route('/index', methods=['GET'], endpoint='index')
@loginDecorator
def index():
return 'xwx'
4.2 路由转换器
from werkzeug.routing import DEFAULT_CONVERTERS
#: the default converter mapping for the map.
DEFAULT_CONVERTERS: t.Mapping[str, t.Type[BaseConverter]] = {
"default": UnicodeConverter,
"string": UnicodeConverter,
"any": AnyConverter,
"path": PathConverter,
"int": IntegerConverter,
"float": FloatConverter,
"uuid": UUIDConverter,
}
转换器 | 类型 | 数据 |
---|---|---|
default | UnicodeConverter | 字符 |
string | UnicodeConverter | 字符 |
any | AnyConverter | 空 |
path | PathConverter | 路径 |
int | IntegerConverter | 整数 |
float | FloatConverter | 小数 |
uuid | UUIDConverter | uuid |
使用
@app.route('/home/<int:uid>', endpoint='home', methods=['GET'])
def home(uid): # 接收一个参数
print(uid)
return render_template('home.html')
4.3 路由源码分析
- 装饰器分析
@
是 python 特殊语法糖,会把下面的函数当参数传入。如下有俩个装饰器的情况下,优先执行最上层的装饰器,也就是包在最外层的装饰器。
@app.route('/index', endpoint='index') # 先执行
@loginDecorator # 后执行
def index():
return 'xwx'
- 源码分析
def route(self, rule: str, **options: t.Any) -> t.Callable:
def decorator(f: t.Callable) -> t.Callable:
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
# 最终执行的是 add_url_rule 函数
@setupmethod
def add_url_rule(
self,
rule: str,
endpoint: t.Optional[str] = None,
view_func: t.Optional[t.Callable] = None,
provide_automatic_options: t.Optional[bool] = None,
**options: t.Any,
) -> None:
raise NotImplementedError
- 参数分析
参数 | 说明 |
---|---|
rule | URL规则 |
view_func | 视图函数名称 |
endpoint | 名称,用于反向生成URL,即: url_for(‘名称’) |
methods | 允许的请求方式,如:[“GET”, “POST”] |
strict_slashes | 对URL最后的 / 符号是否严格要求 |
redirect_to | 重定向到指定地址 |
subdomain | 子域名访问 |
defaults | 默认值, 当URL中无参数,函数需要参数时,使用defaults = {‘k’: ‘v’}为函数提供参数 |
所以注册路由的另外一种方式是
app.add_url_rule('/order/<string:pk>', view_func=order)
app.add_url_rule('/index', view_func=index,)
5 基于类的视图
class Home(MethodView):
methods = ['GET']
# decorators = (装饰器名字,装饰器名字2,)
def get(self):
return 'get'
def post(self):
return 'post'
app.add_url_rule('/home', view_func=Home.as_view(name='home'))
1. 'as_view' 的name是别名,之前写fbv的时候,endpoint是别名,在cbv中name就是endpoint,但
是必须写,即便写了endpoint
2. 'Home.as_view' 是view函数的内存地址,请求来了执行 self.dispatch_request(),MethodView
重写了dispatch_request,其根据请求方式执行视图类中跟请求方式同名的方法,如果视图类继承了View
需要重写dispatch_request
3. cbv加装饰器,在类中写 decorators = (装饰器名字1, 装饰器名字2,),第一个位置的会放在最下层
多个装饰器执行顺序是从【上往下执行】
4. cbv只允许某个请求方式 methods=['POST']
6. 模板
flask 的模板语法使用第三方 jinja2,兼容 dtl,但是它可以加括号,可以使用 [ ],且处理了xss攻击
6.1 渲染变量、变量的循环
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户列表</h1>
<table>
{% for k,v in user_dict.items() %}
<tr>
<td>{{k}}</td>
<td>{{v.name}}</td>
<td>{{v['name']}}</td>
<td>{{v.get('name')}}</td>
<td><a href="/detail/{{k}}">查看详细</a></td>
</tr>
{% endfor %}
</table>
</body>
</html>
6.2 逻辑判断
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户列表</h1>
<table>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello World!</h1>
{% endif %}
</table>
</body>
</html>
6.3 Markup
除了使用 safe
外还可以使用 Markup ,其等价于 django的 mark_safe 。防止 xss 攻击是使用了 html的特殊字符替换(例如将 < 替换成 < 、> 替换成 > ),但是如果使用了 |safe 或者 Markup ,就不会渲染到页面上
from flask import Flask,render_template,Markup,jsonify,make_response
app = Flask(__name__)
def func1(arg):
return Markup("<a href="http://www.baidu.com">点我看美女</a>")
@app.route('/')
def index():
return render_template('index.html',ff = func1)
if __name__ == '__main__':
app.run()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ff()}}
{{ff()|safe}}
</body>
</html>
7. 请求响应
7.1 请求对象的属性和方法
属性 | 说明 | 数值/类型 |
---|---|---|
request.method | 请求方式 | str |
request.args | get 请求参数 | ImmutableMultiDict([]) |
request.form | post 提交的数据 | ImmutableMultiDict([]) |
request.values | get,post提交的数据总和 | CombinedMultiDict([ImmutableMultiDict([]), ImmutableMultiDict([])]) |
request.cookies | cookies | |
request.headers | 请求头(等同于django的META) | <class ‘werkzeug.datastructures.EnvironHeaders’> |
request.path | 路径 | /home |
request.full_path | 全路径 | /home?name=xwx |
request.script_root | ||
request.url | 带域名带参数的请求路径 | http://127.0.0.1:5000/home?name=xwx |
request.base_url | 带域名请求路径 | http://127.0.0.1:5000/home |
request.url_root | 域名+端口 | http://127.0.0.1:5000/ |
request.host_url | 域名+端口 | http://127.0.0.1:5000/ |
request.host | 不带http的域名+端口 | 127.0.0.1:5000 |
request.files | 文件 | ImmutableMultiDict([(‘files’, <FileStorage: ‘20200917170453_31512.jpeg’ (‘image/jpeg’)>)]) <class ‘werkzeug.datastructures.ImmutableMultiDict’> |
request.files.get(‘files’) | 文件 | <class ‘werkzeug.datastructures.FileStorage’> |
7.2 响应对象的属性和方法
属性 | 说明 |
---|---|
“字符串” | 返回字符串 |
render_template(‘html模板路径’,**{}) | 返回模板 |
redirect | 重定向 |
jsonify | 返回 JSON 数据 |
8. cookie、session
cookie:存放在客户端的键值对
session:存放在客户端的键值对
token:存放在客户端,通过算法来校验
8.1 添加 cookie、session
- cookie
使用 make_response 包裹响应对象
response = make_response(render_template('index.html'))
# response 是 flask.wrappers.Response类型
response.delete_cookie('key')
response.set_cookie('key', 'value')
response.headers['X-Something'] = 'A value' # 可用于跨域
return response
- session
from flask import session
除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。 (app.session_interface对象)
设置:session['username'] = 'xxx'
删除:session.pop('username', None)
在使用 session 之前必须现在设置一下密钥 secret_key
app.secret_key="asdas" # 值随便
- django 与 flask 的不同
在django中
1. 生成一个随机的字符串
2. 往数据库存
3. 写入cookie返回浏览器
在 flask 中他没有数据库,但 session 是怎样实现的?
1. 生成一个密钥写入这个cookie
2. 然后下次请求的时候,通过这个cookie解密,然后赋值给session
#可以通过 app.session_interface 来查看
8.2 flask 的 session 源码分析
- 从
app.session_interface
起手
session_interface = SecureCookieSessionInterface()
# 发现 session_interface 是 SecureCookieSessionInterface 的对象。
# 该类中有 open_session、save_session 方法
- open_session 方法
open_session方法当请求来了,从cookie中取出字符串,把字符串反序列化成session对象
def open_session(
self, app: "Flask", request: "Request"
) -> t.Optional[SecureCookieSession]:
s = self.get_signing_serializer(app)
if s is None:
return None
val = request.cookies.get(self.get_cookie_name(app))
if not val:
return self.session_class()
max_age = int(app.permanent_session_lifetime.total_seconds())
try:
data = s.loads(val, max_age=max_age)
return self.session_class(data)
except BadSignature:
return self.session_class()
# 从 cookie 中取出字符串
val = request.cookies.get(self.get_cookie_name(app))
# get_cookie_name 获取的是 SESSION_COOKIE_NAME 默认配置的 session 键名,可以修改
# 反序列化成 session 对象
data = s.loads(val, max_age=max_age)
- save_session
save_session 方法请求走了,把 session 对象,序列化成字符串,放到 cookie 中
def save_session(
self, app: "Flask", session: SessionMixin, response: "Response"
) -> None:
name = self.get_cookie_name(app)
domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app)
secure = self.get_cookie_secure(app)
samesite = self.get_cookie_samesite(app)
if not session:
if session.modified:
response.delete_cookie(
name, domain=domain, path=path, secure=secure, samesite=samesite
)
return
if session.accessed:
response.vary.add("Cookie")
if not self.should_set_cookie(app, session):
return
httponly = self.get_cookie_httponly(app)
expires = self.get_expiration_time(app, session)
val = self.get_signing_serializer(app).dumps(dict(session)) # type: ignore
# 设置 cookie
response.set_cookie(
name,
val, # type: ignore
expires=expires,
httponly=httponly,
domain=domain,
path=path,
secure=secure,
samesite=samesite,
)
- app.session_interface 中 save_session 的参数(设置cookie的参数)
参数 | 说明 |
---|---|
key | 键 |
value | 值 |
max_age=None | 超时时间 cookie需要延续的时间(以秒为单位)如果参数是 None ,这个cookie会延续到浏览器关闭为止 |
expires=None | 超时时间(IE requires expires, so set it if hasn’t been already.) |
path=‘/’ | Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问,浏览器只会把cookie回传给带有该路径的页面,这样可以避免将cookie传给站点中的其他的应用。 |
domain=None | Cookie生效的域名 你可用这个参数来构造一个跨站cookie。如, domain=".example.com"所构造的cookie对下面这些站点都是可读的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。如果该参数设置为 None ,cookie只能由设置它的站点读取 |
secure=False | 浏览器将通过HTTPS来回传cookie |
httponly=False | 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖) |
9. 闪现 message
闪现是由 flash 翻译过来的。一般用在当 a 页面操作出错,跳转到 b 页面,在 b 页面显示 a 页面的错误信息。其本质就是放到session中
,等同于 django 的 message 框架
基础使用
设置: flash()
取值: get_flashed_message()
from flask import Flask, flash, get_flashed_messages, redirect
app = Flask(__name__)
app.secret_key = 'asdfasdf'
app.debug = True
@app.route('/index')
def index():
# flash('超时错误')
# 添加分类
flash('超时错误', category="x1")
return redirect('/error')
@app.route('/error')
def error():
"""
展示错误信息
:return:
如果get_flashed_messages(with_category=True)
"""
# msg = get_flashed_messages()
# 添加分类,取出的值是列表
msg = get_flashed_messages(category_filter=['x1'])
return "错误信息:%s" % msg[0]
if __name__ == '__main__':
app.run()
注意点: 在一次请求中,把一些数据放在闪现中,下次请求就可以从闪现中取出来,取一次就没了(使用分类也是这样)
10. 请求扩展
10.1 before_request
类比 django 中间件中的 process_request
,在请求收到之前绑定一个函数做一些事情
若其返回的是响应对象,则不继续往下走了,如果返回 None,继续走下一个请求扩展
- 请求来了,就会执行它
- 多个 before_request,会从上往下依次执行
# 基于它做用户登录认证
@app.before_request
def process_request(*args, **kwargs):
if request.path == '/login':
return None
try:
session['user_info']
return None
except KeyError as e:
return redirect('/login')
10.2 after_request
类比 django 中间件中的 process_response
,每一个请求之后绑定一个函数,如果请求没有异常
- 必须返回 response 对象
- 请求走了,会执行它
- 多个 after_request,从下往上依次执行
@app.after_request
def p1(response):
print(111)
print(response)
return response
@app.after_request
def p2(response):
print(222)
print(response)
return response
输出:
>> 222
>> <Response 5 bytes [200 OK]>
>> 111
>> <Response 5 bytes [200 OK]>
10.3 before_first_request
启动后第一次请求时,跟浏览器无关。返回 None,会继续往下执行
@app.before_first_request
def first():
pass
10.4 teardown_request
每一个请求之后绑定一个函数,即使遇到了异常。无论程序是否出异常,都会触发它,可以用来记录错误日志
@app.teardown_request
def ter(e):
# 打印的错误信息,注意需要将 debug 设置为 False
print(e)
return None
10.5 errorhandler
监听某个状态码,如果是这个状态码的错误,就会触发它的执行。可以返回响应对象例如 404 界面
@app.errorhandler(404)
def error_404(e):
# 错误信息
print(e)
return '该页面不存在'
10.6 template_global
标签
@app.template_global()
def add(a, b):
return a + b
# 在模板中使用 {{add(1,2)}}
10.7 template_filter
过滤器
@app.template_filter()
def add(a, b):
return a + b
# 在模板中使用 {{3|add(1)}}
10.8 总结
1 重点掌握before_request和after_request,
2 注意有多个的情况,执行顺序
3 before_request请求拦截后(也就是有return值),response所有都执行
@app.before_request
def req1():
print('这是请求1')
return '这是请求1'
@app.before_request
def req2():
print('这是请求2')
return '这是请求2'
@app.after_request
def res1(response):
print('这是响应1')
return response
@app.after_request
def res2(response):
print('这是响应2')
return response
输出:
>> 这是请求1
>> 这是响应2
>> 这是响应1
11. 蓝图
蓝图是由 Blue_print 翻译过来的,作用是对目录进行划分
11.1 使用蓝图之中小型系统
目录结构:
flaskProject # 项目名
flaskProject # 包名
static # 静态文件夹
templates # 模板文件夹
views # 视图函数文件夹
user.py # 用户相关视图函数
goods.py # 商品相关视图函数
__init__.py # 包 init 文件
run.py # 项目启动文件
- _init_
from flask import Flask
from flaskProject.views import user
from flaskProject.views import goods
# 注册app,指定模板文件路径、静态文件路径以及路径后缀
app = Flask(__name__, template_folder='templates', static_folder='static', static_url_path='/static')
# 在app中注册蓝图
app.register_blueprint(user.user)
app.register_blueprint(goods.good)
- user.py
from flask import Blueprint
user = Blueprint('user', __name__)
# 使用蓝图注册路由
@user.route('/login')
def login():
return 'login'
- run.py
from flaskProject import app
if __name__ == '__main__':
# 指定运行的IP与端口
app.run(host='0.0.0.0', port=8080)
11.2 使用蓝图之大型系统
目录结构:
flaskProject_big # 项目名
flaskProject_big # 包名
goods # app 名
static # app 独立静态文件
templates # app 独立模板
__init__.py # app 双下 init
views.py # app 独立视图函数
user # app 名
static # app 独立静态文件
templates # app 独立模板
__init__.py # app 双下 init
views.py # app 独立视图函数
__init__.py # 包 双下init
run.py # 启动文件
- flaskProject_big/user/init.py
from flask import Blueprint
# 第一步:初始化蓝图
user = Blueprint(
'user',
__name__,
template_folder='templates', # 指定该蓝图对象自己的模板文件路径
static_folder='static', # 指定该蓝图对象自己的静态文件路径
static_url_path='/static',
)
# 导入视图后路由才能被匹配,且不能放在前面导入
from . import views
- flaskProject_big/user/views.py
# 导入蓝图
from . import user
from flask import render_template
@user.before_request
def before():
print('user 的 before')
# 第三步:使用蓝图注册路由
@user.route('/login')
def login():
return render_template('login.html')
- flaskProject_big/user/templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<body>
<img src="static/m.jpeg">
</body>
</html>
- flaskProject_big/init.py
from flask import Flask
from .goods import goods
from .user import user
app = Flask(__name__)
app.debug = True
# 第二步:在app中注册蓝图
# url_prefix='/user' 设置访问这个app的前缀,等同于django include
app.register_blueprint(user, url_prefix='/user')
app.register_blueprint(goods)
- run.py
from flaskProject_big import app
if __name__ == '__main__':
app.run(port=8080)
总结:
- 第一步,在 app 的双下 init 中初始化蓝图,并导入视图
- 第二步,在 包 的双下 init 中注册蓝图,可以设置 url_prefix 路由前缀
- 第三步,在 app 的视图文件中注册路由,编写视图函数即可
注意点:
- 加了 url_prefix (蓝图URL前缀)后,在该蓝图下所有 url 都加前缀
- 初始化蓝图时,template_folder 给当前蓝图单独使用 templates,若当前找不到,会向上查找,找总templates
- 蓝图的请求拓展例如 befort_request,只对当前蓝图有效
- 大型项目,可以模拟出类似于 django 中 app 的概念
12. g 对象
专门用来存储用户信息的g对象,g的全称的为global。不放在 request中是为了防止值被覆盖掉
# 请求拓展
@user.before_request
def before():
g.name = 'xwx'
@user.route('/login')
def login():
print(g.name, type(g))
return render_template('login.html')
输出:
>> xwx <class 'werkzeug.local.LocalProxy'>
g 对象和 session 的区别
- session 对象是可以跨 request 的,只要 session 还未失效,不同的 request 的请求会获取到同一个 session
- 但是 g 对象不是,g 对象不需要管过期时间,请求一次就 g 对象就改变了一次,或者重新赋值了一次,也就是 g 对象只对当次请求有效
13. flask-session
session 默认以 cookie 的形式放到浏览器中,若把 session 放到服务端保存( redis 、mysql 中等等)
作用:将默认保存的签名 cookie 中的值保存到 redis/memcached/file/Mongodb/SQLAlchemy
安装:pip3 install flask-session
Django 存放到 redist 参考:https://blog.csdn.net/weixin_42194215/article/details/111414823
13.1 使用方式一
from flask import Flask,session
from flask_session import RedisSessionInterface
import redis
app = Flask(__name__)
conn=redis.Redis(host='127.0.0.1',port=6379)
# key_prefix 表示在 redis 的前缀
app.session_interface=RedisSessionInterface(conn,key_prefix='xwx_')
@app.route('/')
def hello_world():
session['name']='xwx'
return 'Hello World!'
if __name__ == '__main__':
app.run()
13.2 使用的方式二
from redis import Redis
from flask_session import Session
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = Redis(host='127.0.0.1',port='6379')
# 用类包裹,常见写法
Session(app)
问题:设置cookie时,如何设定关闭浏览器则cookie失效。
response.set_cookie('k','v',exipre=None)#这样设置即可
#在session中设置
app.session_interface=RedisSessionInterface(conn,key_prefix='lqz',permanent=False)
#一般不用,我们一般都设置超时时间,多长时间后失效
问题:cookie默认超时时间是多少?如何设置超时时间
#源码expires = self.get_expiration_time(app, session)
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),#这个配置文件控制