Flask篇:session的使用和原理、闪现、请求拓展、蓝图、g对象、flask-session

一、session的使用和原理

1.1 session执行原理

        用户第一次请求后,将产生的状态信息保存在session中,这时可以把session当做一个容器,它保存了正在使用的所有用户的状态信息;这段状态信息分配了一个唯一的标识符用来标识用户的身份,将其保存在响应对象的cookie中;当第二次请求时,解析cookie中的标识符,拿到标识符后去session找到对应的用户的信息。
image

1.2 简单使用

from flask import Flask,session
app = Flask(__name__)
@app.route('/test1/')
def test():
    session['name'] =  'xiaoming'
    return 'OK'
if __name__ == '__main__':
    app.run(host='127.0.0.1', port=80, debug=True)

1.3 flask的session源码分析

# 1.Flask源码中process_response
    def process_response(self, response: Response) -> Response:
		......
        if not self.session_interface.is_null_session(ctx.session):
            self.session_interface.save_session(self, ctx.session, response)
        return response
    
    
# 2.session_interface是SecureCookieSessionInterface()类的对象
	session_interface: SessionInterface = SecureCookieSessionInterface()


# 3.SecureCookieSessionInterface()类有两个方法
	-open_session:请求来了,从cookie中取出字符串,把字符串反序列化成session对象
    -save_session:请求走,把session对象,序列化成字符串,放到cookie中
    
    
# open_session
    def open_session(self, app, request):
        s = self.get_signing_serializer(app)
        if s is None:
            return None
        val = request.cookies.get(app.session_cookie_name)
        if not val:
            return self.session_class()
        max_age = total_seconds(app.permanent_session_lifetime)
        try:
            data = s.loads(val, max_age=max_age)
            return self.session_class(data)
        except BadSignature:
            return self.session_class()
        
# save_session 
    def save_session(self, app, session, response):
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)
        if not session:
            if session.modified:
                response.delete_cookie(
                    app.session_cookie_name, domain=domain, path=path
                )

            return
        if session.accessed:
            response.vary.add("Cookie")

        if not self.should_set_cookie(app, session):
            return

        httponly = self.get_cookie_httponly(app)
        secure = self.get_cookie_secure(app)
        samesite = self.get_cookie_samesite(app)
        expires = self.get_expiration_time(app, session)
        val = self.get_signing_serializer(app).dumps(dict(session))
        response.set_cookie(
            app.session_cookie_name,
            val,
            expires=expires,
            httponly=httponly,
            domain=domain,
            path=path,
            secure=secure,
            samesite=samesite,
        )

二、flash(闪现)

1.闪现的含义

        flash音译过来的结果,可以在一个请求结束后记录信息,然后在下一次请求中展示。

# 假设在a页面操作出错,跳转到b页面,在b页面显示a页面的错误信息
    -本质其实就是在a页面,把错误信息放到了某个位置,在b页面把错误取出来

2.闪现的使用

    -放值
    	flash(err)
        flash(err,category='lqz')  # category参数可以将信息进行分类
    -取值
    	get_flashed_messages()
        err = get_flashed_messages(category_filter=['lqz'])  # category_filter过滤分类信息

3.闪现的特点

        在一次请求中,把一些数据放在闪现中,下次请求就可以从闪现中取出来,取一次就没了(使用分类也是这样)

4.闪现的本质

        闪现所记录的信息实际上是存在session中的,但是只能取一次,均在flash(存)和get_flashed_messages(取)的源码中有所体现

三、Flask请求拓展(装饰器)

1.作用

        类似于django的中间件,不同的装饰器可以在请求的不同阶段进行拦截和操作

2.使用

2.1 before_request

from flask import Flask
app = Flask(__name__)

# @app.before_request
    -return四件套,不继续往下走了,如果retrun None,继续走下一个请求扩展
    -请求来了,就会执行它
    -多个before_request,会从上往下依次执行

    # 示例:
    @app.before_request  # 类似于,django的中间件的process_request,黑白名单机制
    def before1():
        # 所有人都不允许访问index
        if request.path == '/':
            return '不允许访问index'  #return四件套,不继续往下走了,如果retrun None,继续走下一个请求扩展
        print('before_request1111')

    @app.before_request
    def before2():
        print('before_request2222')   

2.2 after_request

# @app.after_request
    -必须返回response对象
    -请求走了,会执行它
    -多个after_request,从下往上依次执行

    # 示例:
    @app.after_request
    def after1(response):
        print('after11')
        print(response)
        return response  # 响应对象

    @app.after_request
    def after1(response):
        print('after2222')
        return response  # 响应对象

2.3 before_first_request

# @app.before_first_request
    -项目启动后,第一次访问,会执行
    -返回None,会继续往下执行,返回四件套,就不继续往后走了

    # 示例:
    @app.before_first_request
    def first():
        print('我的第一次')

2.4 teardown_request

# @app.teardown_request
    -无论程序是否出异常,都会触发它
    -记录错误日志
    -注意,只有app.debug=False模式才行

    # 示例:
    @app.teardown_request
    def tear_down(e):
        print(e)  # 如果有错,e就是错误对象,如果没错,e就是空的
        print('teardown_request')
        # 没法返回统一的页面,四件套不能用

2.5 errorhandler

# @app.errorhandler
    -监听某个状态码,如果是这个状态码的错误,就会触发它的执行
    -可以返回四件套,统一返回格式
    # 示例:
    @app.errorhandler(404)
    def errorhandl(arg):
        print('页面没找到')
        print(arg)
        return render_template('404.html')

    @app.errorhandler(500)
    def errorhandl(arg):
        print(arg)
        return jsonify({'code': 999, 'msg': '请联系系统管理员'})

2.6 template_global

# @app.template_global:标签
    # 定义
    @app.template_global()
    def sb(a1, a2):
        return a1 + a2

    # 模板中使用
    {{sb(1,2)}}

2.7 template_filter

# @app.template_filter:过滤器
    # 定义
    @app.template_filter()
    def db(a1, a2, a3):
        return a1 + a2 + a3

    # 模板中使用
    {{ 1|db(2,3)}}

总结
1 重点掌握before_request和after_request,
2 注意有多个的情况,执行顺序
3 before_request请求拦截后(也就是有return值),response所有都执行 ---》django一样

四、蓝图

1.蓝图的作用

        在进行flask开发的时候,需要按照不同的封装对象进行开发。比如我们在开发电影网站,可以按照电影的url,用户的url,评论的url来进行开发,避免了将整个url都放入一个文件中,此时我们就需要使用到flask中的蓝图

2.不使用蓝图的目录

缺点
        全局就一个app对象,导来导去,很容易出现循环导入的问题

    -templates
    -views
        -__init__.py
        -user.py
        -order.py
    -app.py

3.使用蓝图

3.1 使用步骤

(1) 实例化得到蓝图对象 ---可以传一些参数:account = Blueprint('account', __name__,templates,static)
(2) 把蓝图对象在app中注册---》app.register_blueprint(account,制定前缀)
(3) 使用蓝图对象,注册路由
(4) 蓝图有自己的请求扩展--》app对象总的请求扩展,每个蓝图有自己的请求扩展

3.2 使用蓝图小型项目目录划分

-flask_pro               #项目名
    -pro_flask          # 文件夹
        -__init__.py    # 包的init文件
        -static          #静态文件
        	-code.png
        -templates       #模板文件
        	-index.html
        -views           #视图函数写在里面
            -account.py    #订单相关视图函数
            -blog.py     #博客相关视图函数
    -manage.py          # 项目的启动文件

pro_flask/init.py

from flask import Flask

app = Flask(__name__,template_folder='templates',static_folder='statics',static_url_path='/static')

from .views.account import account
from .views.blog import blog

# 注册3个蓝图,把他们注册进app中
app.register_blueprint(account)
app.register_blueprint(blog)

pro_flask/views/account.py

from flask import Blueprint
from flask import render_template
# 实例化得到蓝图对象
account = Blueprint('account', __name__)

@account.route('/login.html', methods=['GET', "POST"])
def login():
    return render_template('login.html')

pro_flask/views/blog.py

from flask import Blueprint, render_template,request

blog = Blueprint('blog', __name__)

@blog.route('/get_blog')
def get_blog():
    print(request.path)
    return render_template('blog.html')

manage.py

from pro_flask import app
if __name__ == '__main__':
    app.run()

3.3 使用蓝图大型项目目录划分

pro_flask   # 项目名
    -pro_flask #包名
        -admin #admin  app的名字
            -static #app自己的静态文件
            -templates #app自己的模板文件
            -__init__.py #包的init
            -views.py   #app自己的视图函数
        -web  #web   app的名字
            -static  #app自己的静态文件
            -templates #app自己的模板文件
            -__init__.py #包的init
            -views.py  #app自己的视图函数
        -__init__.py
    -run.py

pro_flask/admin/init.py


from flask import Blueprint

# 第一步:初始化蓝图
admin = Blueprint(
    'admin',
    __name__,
    template_folder='templates',  # 指定该蓝图对象自己的模板文件路径
    static_folder='static'       # 指定该蓝图对象自己的静态文件路径
)


from . import views

pro_flask/admin/views.py

from . import admin
from flask import render_template
@admin.before_request
def before():
    print('admin 的 before')
# 第三步:使用蓝图注册路由
@admin.route('/index')
def index():
    return render_template('admin.html')

pro_flask/web/init.py

from flask import Blueprint
web = Blueprint(
    'web',
    __name__,
    template_folder='templates',
    static_folder='static'
)
from . import views

pro_flask/web/views.py

from . import web
@web.route('/index')
def index():
    return 'Web.Index'

pro_flask/init.py

from flask import Flask
from .admin import admin
from .web import web

app = Flask(__name__)
app.debug = True

# 第二步:在app中注册蓝图
app.register_blueprint(admin, url_prefix='/admin') # url_prefix='/admin' 访问这个app的前缀,等同于include
app.register_blueprint(web)


run.py

from pro_flask import app

if __name__ == '__main__':
    app.run()

五、g对象

5.1 什么是g对象

         flask 中有个 g 对象,在一次请求中,可以赋值,取值;
         g对象是当次请求中得全局对象,在一次请求中放了值,后续就可以取出来;
         为什么不放在request中:为了防止值被覆盖掉;

5.2 g和session的区别

    -g对象只对当次请求有效
    -session对当前用户所有请求都有效

5.3 g对象的使用

from flask import Flask, g, request,session

app = Flask(__name__)
app.debug=True


@app.before_request
def before():
    print(type(g))  # werkzeug.local.LocalProxy    代理模式
    print(type(session))  # werkzeug.local.LocalProxy
    print(type(request))  # werkzeug.local.LocalProxy
    if request.path == '/':
        # request.method='lqz'
        g.name = 'lqz'  # 放到g对象中
    else:
        # request.name = 'pyy'
        g.name = 'pyy'

def add(a,b):
    print(g.name)
    # print('-----', request.name)

@app.route('/')
def index():
    print(g.name)
    # print('-----',request.name)
    # add(1,2)
    return 'hello'


@app.route('/home')
def home():
    print(g.name)
    return 'hello'


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

六、flask-session

6.1 为什么使用flask-session

session默认以cookie的形式放到浏览中,我们想把session,放到服务端保存(redis中,mysql中。。。)

通过上文中的1.3,我们了解了flask中session的执行原理,因此我们只需要写一个类,写open_session和save_session方法,就可以对session的存取进行操作

6.2 flask-session的使用方式(源码分析可得)

from flask import Flask, session

app = Flask(__name__)
app.debug=True
app.secret_key='asdasdfasdf'

# 使用flask-session:方式一:
from flask_session import RedisSessionInterface
app.session_interface=RedisSessionInterface(redis=None,key_prefix='luffy_')

# 方式二:通用方案
from redis import Redis
from flask_session import Session
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_KEY_PREFIX'] = 'luffy'
app.config['SESSION_REDIS'] = Redis(host='127.0.0.1',port='6379')
Session(app)  #后面会经常见这种写法--->类(app)---》包一下的写法


@app.route('/set_session')
def set_session():
    session['name']='lqz'
    return '设置成功'

@app.route('/get_session')
def get_session():
    print(session['name'])
    return '获取成功'


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)
posted @ 2022-08-09 16:00  马氵寿  阅读(452)  评论(0编辑  收藏  举报