CBV分析、模板、请求与响应、session及源码分析、闪现、请求扩展
一、CBV分析
1、基于类的视图写法
from flask import Flask,request from flask.views import View, MethodView app = Flask(__name__) app.debug = True # 视图类,继承MethodView,类中写跟请求方式同名的方法即可,之前学的所有都一致 class IndexView(MethodView): def get(self): print(request.method) return 'get 请求' def post(self): print(request.method) return 'post 请求' app.add_url_rule('/index', endpoint='index', view_func=IndexView.as_view('index')) if __name__ == '__main__': app.run()
2、源码分析
1、IndexView.as_view(‘index’)执行完的结果,是个函数(view的)内存地址
def as_view(cls, name, *class_args, **class_kwargs): def view(**kwargs: t.Any) -> ft.ResponseReturnValue: # 本质是在执行self.dispatch_request,只是用了异步 return current_app.ensure_sync(self.dispatch_request)(**kwargs) return view
2、请求来了,执行view()--->本质在执行self.dispatch_request---》MethodView中的
def dispatch_request(self, **kwargs): # self是视图类的对象 meth = getattr(self, request.method.lower(), None) # 用异步执行meth() return current_app.ensure_sync(meth)(**kwargs)
3、路径如果不传别名,别名就是函数名---》分析一下源码
@app.route('/index')--》没有传endpoint endpoint 就是None---》调用了app.add_url_rule,传入了None if endpoint is None: endpoint = _endpoint_from_view_func(view_func) # type: ignore _endpoint_from_view_func 就是返回函数的名字
4、as_view(‘index’)必须传参数,传进来的参数是【别名】
# view是as_view内的内层函数,闭包函数 view.__name__ = name # 修改了函数的名字变成了你传入的 # app.add_url_rule('/index',view_func=IndexView.as_view('index')) 简写成:app.add_url_rule('/index',view_func=view) #如果不传参数, 所有人的别名(endpoint),都是内层函数view,所以就报错了
5、视图类必须继承MethodView,如果继承View,它的dispatch_request没有具体实现,你的视图类必须重写dispatch_request,我们不想重写,继承MethodView
def dispatch_request(self) -> ft.ResponseReturnValue: raise NotImplementedError()
6、视图类加装饰器,直接配置在类属性上【decorators】即可
decorators = [auth,] # 源码,cls是视图类,中有decorators if cls.decorators: for decorator in cls.decorators: view = decorator(view) # view=auth(view)
7、补充
flask的路由注册使用装饰器,如果写了一个登录认证装饰器,那么应该放在路由装饰器上还是下?
放在路由下面 路由必须传endpoint,如果不传,又报错
源码学到的
-1 as_view 执行流程跟djagno一样 -2 路径如果不传别名,别名就是函数名(endpoint) -3 视图函数加多个装饰器(上下顺序和必须传endpoint) -4 视图类必须继承MethodView,否则需要重写dispatch_request -5 视图类加装饰器:类属性decorators = [auth,]
二、模板
py文件
from flask import Flask, render_template,Markup app = Flask(__name__, template_folder='templates', static_folder='static') # 模板的路径必须是templates,因为实例化app对象时,传入的 app.debug=True def add(a,b): return a+b @app.route('/') def index(): a='<a href="http://www.baidu.com">点我看美女</a>' # 不存在xss攻击,处理了xss a=Markup(a) return render_template('index.html',name='lqz',a=a,add=add) if __name__ == '__main__': app.run()
html文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>模板语法,static</h1> <img src="/static/1.jpg" alt=""> <h1>模板语法,if</h1> {% if name %} <h1>Hello {{ name }}!</h1> {% else %} <h1>Hello World!</h1> {% endif %} <h1>模板语法,标签渲染</h1> {{a|safe}} {{a}} <h1>模板语法,执行函数</h1> {{add(4,5)}} </body> </html>
三、请求与响应
请求:全局的request对象
请求名称 | 作用 |
---|---|
request.method | 提交的方法 |
request.args | get请求体积的数据 |
request.form | post请求提交的数据 |
request.values | post和get提交的数据总和 |
request.cookies | 客户端所带的cookie |
request.headers | 请求头 |
request.path | 不带域名,请求路径 |
request.full_path | 不带域名,带参数的请求路径 |
request.script_root | 请求的完整URL,包括查询字符串 |
request.url | 带域名带参数的请求路径 |
request.base_url | 带域名请求路径 |
request.url_root | 域名(请求的完整URL,包括查询字符串) |
request.host_url | 域名(完整的应用程序URL,包括协议和端口) |
request.host | 127.0.0.1:5000 |
request.files | 文件的数据 |
响应:四件套
我们之前学习了四件套,也就是四种返回数据的方式
return "字符串" return render_template('html模板路径',**{}) return redirect('/index.html') return jsonify({'k1': 'v1'})
从本质上来说,其实就是这些方法把数据封装到response对象中去,然后把response对象返回了出去
而我们很多情况下会出现下列需求:
1、响应中写入cookie 2、响应头中写数据(如果用了新手四件套,都要用make_response包一下)
点击查看代码
from flask import Flask, request, make_response,render_template app = Flask(__name__) app.debug = True @app.route('/', methods=['GET', 'POST']) def index(): #### 请求 # request.method 提交的方法 # request.args get请求提及的数据 # request.form post请求提交的数据 # request.values post和get提交的数据总和 # request.cookies 客户端所带的cookie # request.headers 请求头 # request.path 不带域名,请求路径 # request.full_path 不带域名,带参数的请求路径 # request.script_root # request.url 带域名带参数的请求路径 # request.base_url 带域名请求路径 # request.url_root 域名 # request.host_url 域名 # request.host 127.0.0.1:500 print(request.method) print(request.args) print(request.form) print(request.values) print(request.cookies) print(request.headers) print(request.path) print(request.full_path) print(request.url) print(request.base_url) print(request.host_url) print(request.host) obj = request.files['file'] obj.save(obj.filename) ### 响应 四件套 # 1 响应中写入cookie # response = 'hello' # res = make_response(response) # flask.wrappers.Response # print(type(res)) # res.set_cookie('xx','xx') # return res # 2 响应头中写数据(新手四件套,都用make_response包一下) response = render_template('index.html') res = make_response(response) # flask.wrappers.Response print(type(res)) res.headers['yy']='yy' return res if __name__ == '__main__': app.run()
四、session及源码分析
1、session的使用
实现的功能:用户去登录,登录成功后,访问index页面可以在该页面展示出登录用户的名称,如果没登录就让页面展示匿名用户
app.py
from flask import Flask, request, session, render_template, redirect app = Flask(__name__) app.debug = True app.secret_key = 'dasdqawedrasdfc' @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'GET': return render_template('login.html') else: name = request.form.get('name') password = request.form.get('password') print(password) session['name'] = name return redirect('/index') @app.route('/index', methods=['GET', 'POST']) def index(): return 'hello %s' % session.get('name', '匿名用户') if __name__ == '__main__': app.run()
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="post"> <p>用户名:<input type="text" name="name"></p> <p>密码:<input type="text" name="password"></p> <p><input type="submit" value="提交"></p> </form> </body> </html>
2、源码分析
cookie:存在于客户端浏览器的键值对
session:存在于服务端的键值对(django放在了django_session表中)
flask中叫做session,问题来了,session是存在那里了?
''' 加密后,放到了cookie中,如果session发生了变化,我们的cookie也会跟着变 '''
源码部分:
1、首先我们要引出一个属性:app.session_interface
它配置了一个类的对象,这个就是session的执行流程
2、我们查看session_interface的源码
session_interface: SessionInterface = SecureCookieSessionInterface()
3、接着我们去看SecureCookieSessionInterface的源码
首先我们会发现他是一个类,但是他不是重点,重点是他内部的open_session、save_session两个方法
请求来了,会执行open_session
请求走了,会执行save_session
open_session源码分析
def open_session(self, app, request): # 1 根据名字,取出前端传入的cookie的value值 val = request.cookies.get(self.get_cookie_name(app)) # 2 如果没有val,构造了一个空session对象 if not val: return self.session_class() max_age = int(app.permanent_session_lifetime.total_seconds()) '这里是获取过期时间' try: # 如果没有过期,解码,做成session对象,后续直接用session即可 data = s.loads(val, max_age=max_age) '这里对cookie中的数据进行了解密' return self.session_class(data) except BadSignature: # 如果过期了,也是空session return self.session_class()
save_session源码分析
def save_session(self, app, session, response): name = self.get_cookie_name(app) '这里跟上面一样,通过cookie中键值对的键,查找session的信息' # 取出过期时间,并把session加密转成字符串,放到cookie中 expires = self.get_expiration_time(app, session) val = self.get_signing_serializer(app).dumps(dict(session)) response.set_cookie( name, val, expires=expires, )
扩展
如果我们想把session放到redis中、mysql中,已经有人帮咱们写了,用第三方的模块即可
当然我们也可以重写一个类来实现这个功能(重写open_session,save_session自己写)
五、闪现(flash)
flash是翻译过来得到闪现这个名称的,他的效果简单描述如下:
当次请求先把一些数据,放在某个位置 下一次请求,把这些数据取出来,取完,就没了
作用:
当次请求先把一些数据,放在某个位置 下一次请求,把这些数据取出来,取完,就没了
djagno中有这个东西吗?
- message框架
flash用法
设置 闪现
-flash('%s,我错了'%name) ,可以设置多次,放到列表中 -flash('超时错误',category="debug") 分类存
获取 闪现
-get_flashed_messages() ,取完就删除 -get_flashed_messages(category_filter=['debug'])分类取
本质:放到session中(使用的时候可以查到的)
六、请求扩展
这里的请求扩展可以类比django的中间件
- 在请求来了,或请求走了,可以绑定一些函数,到这里就会执行这个函数,类似于django的中间件
- 在flask中就用请求扩展,来代替djagno的中间件
请求扩展方法
方法名称 | 触发条件及简介 |
---|---|
before_request | 请求来了会走,如果他返回了四件套,就结束了(类比django中间件中的process_request) |
after_request | 请求走了会走,一定要返回response对象(类比django中间件中的process_response) |
before_first_request | 第一次来请求的时候会走,跟浏览器无关 |
teardown_request | 每一个请求之后绑定的一个函数,即使遇到了异常也会走他 |
errorhandler | 监听状态码,404 500 |
template_global | 标签 |
template_filter | 过滤器 |
总结:
1、重点掌握before_request和after_request,
2、注意有多个的情况,执行顺序
3、before_request请求拦截后(也就是有return值),response所有都执行
操作代码
from flask import Flask, request,render_template app = Flask(__name__) ####1 before_request 和 after_request # 请求来了,执行一个函数,来的时候从上往下执行 # @app.before_request # def before(): # print('我来了111') # # if 'index' in request.path: # return '不让看了' # 如果不是retrun了None,说明被拦截了,直接返回 # # # @app.before_request # def before1(): # print('我来了222') # # # # 请求走了,执行一个函数,走的时候,从下往上执行 # @app.after_request # def after(response): # print('我走了111') # return response # # # @app.after_request # def after2(response): # print('我走了222') # return response # 2 项目启动后的第一个请求 # @app.before_first_request # def first(): # print('我的第一次') # 3 teardown_request,无论视图函数是否出错,都会执行它,做错误日志 # @app.teardown_request # def teardown(e): # print(e) # '这里的e就是报错信息' # print('执行我了') # 4 errorhandler 监听响应状态码,如果符合监听的状态码,就会走它 # @app.errorhandler(404) # def error_404(arg): # return "404错误了" # @app.errorhandler(500) # def error_500(arg): # return "500错误了" ##5 template_global 在模板中直接使用该标签 @app.template_global() def add(a1, a2): return a1 + a2 # 6 template_filter 这个过滤器也是在模版中使用,但是使用方式有讲究 @app.template_filter() def db(a1, a2, a3): return a1 + a2 + a3 @app.route('/') def index(): # a = [1, 2, 3] # print(a[9]) return render_template('index1.html') if __name__ == '__main__': app.run()
index1.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>标签和过滤器</h1> {{add(6,6)}} <br> {{8|db(2,3)}} </body> </html>
我们可以看到过滤器需要先写一个变量,然后再在db中添加两个变量
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?