flask学习1
总结
Flask Web 框架 轻量 websocket 全双工通讯 socket TCP 通讯 MongoDB 数据库 文件型数据库 {} 没有限制 和 约束 Mui + HTML5 Plus 调用移动操作系统的封装 IOS Android 人工智能技术应用 BaiduAI ASR 语音识别 声音转换成文字 TTS 语音合成 文字转换成声音 NLP 自然语言处理 你的名字叫什么 你的名字是什么 文本相似度 paddle paddle 百度 Ai studio 智能玩具 Flask 1.框架对比 Django Flask Admin - Model 原生无 Model 原生无 Form 原生无 Session 有 - 颠覆认知操作 教科书式框架 以简单为基准 开发 一切从简 能省则省 Django的优势: 组件全,功能全,教科书 Django的劣势: 占用资源,创建复杂度较高 Flask的优势: 轻,快 Flask的劣势: 先天不足,第三方组件稳定性较差 2.入门Flask pip3 install Flask **Ps:不要使用工具中的插件创建Flask项目 三行代码启动 Flask 项目 3.Flask 中的 Response 1.HTTPResponse("hello") "" str 2.render 响应模板 render_template("html") str 3.redirect("/") redirect("/") str 以上是Web框架的 Response 三剑客 4.send_file() instance 返回文件内容,自动识别文件类型,Content-type中添加文件类型,Content-type:文件类型 ** 浏览器特性 可识别的Content-type 自动渲染 不可识别的Content-type 会自动下载 5.jsonify() str # 返回标准格式的JSON字符串 先序列化JSON的字典,Content-type中加入 Application/json ** Flask 1.1.1 版本中 可以直接返回字典格式,无需jsonify 4.Flask 中的 请求 Request request.method 获取请求方式 request.form 获取FormData中的数据 也就是所谓的Form标签 to_dict() request.args 获取URL中的数据 to_dict() request.json 请求中 Content-Type:application/json 请求体中的数据 被序列化到 request.json 中 以字典的形式存放 request.data 请求中 Content-Type 中不包含 Form 或 FormData 保留请求体中的原始数据 b"" request.files 获取Form中的文件 request.path 请求路径 路由地址 request.url 访问请求的完整路径包括 url参数 request.host 主机位 127.0.0.1:5000 request.cookies 字典获取浏览器请求时带上的Cookie ** request.values 获取 url 和 FormData 中的数据 敏感地带 5.Jinja2 ---- template语言 {{ }} 引用 或 执行 {% %} 逻辑引用
Flask 入门
1.框架对比 Django Admin 组件- 基于Model实现的,Model,Form。教科书式框架
Flask 无Admin 原生无 Model 原生无 Form Session 有 - 颠覆认知操作。flask虽然原生没有,但是第三方组件特别多。只要有个人写个扩展功能出来,直接放到flask中,flask就能用了。第三方组件多,强大。如果包括第三方组件,那么flask比Django功能还要多。缺点:flask自己是不支持那么多组件的,官方认可,但不代表是他们开发的,所以当flask版本升级了,有可能使得第三方组件用不了了。flask宗旨:以简单为基准 开发 一切从简 能省则省。这个session没有摒弃,因为它占有资源非常少
Django的优势: 组件全,功能全,教科书 Django的劣势: 占用资源,创建(开发)复杂度较高
Flask的优势: 轻,快 Flask的劣势: 先天不足,第三方组件稳定性较差
2.入门Flask
pip3 install Flask **Ps:不要使用工具中的插件创建Flask项目
三行代码启动 Flask 项目
不要勾选继承全局包,不然会复制一份包过来,会很慢,直接创建新的。
安装flask组件
卸载之后换个版本
Flask框架源码,Jinja2模板语言,Django里template是基于Jinja2实现的。MarkupSafe是后端给前端传递字符串,将字符串转为标签来展示在浏览器中使用的。render底层就是基于它。Werkzeug ① 工具 ② (动物的某一)器官 ③ 工具(指人、机构等) .Werkzeug 相当于Django的uwsgi,承载服务用的,类似于tomcat,jboss等web服务,应用程序网关接口,它和uwsgi的底层是一样的,都是wsgi。这些是flask的主要依赖包
创建一个flask项目。启动项目。
from flask import Flask
app=Flask(__name__)
app.run()
成功访问:
在去查看访问情况,可以看到后台获取/,但是资源没有找到,这是应用层抛出的错误。如果服务没起就访问,抛出的就不是404了,比如超时等之类的错误
flask实现hello world
如果@app.route()每加路由,那么报错:TypeError: route() missing 1 required positional argument: 'rule'
from flask import Flask
app=Flask(__name__)
@app.route("/")
def home():
return "hello world I am mcw"
app.run()
/是路由地址,定义视图函数,直接返回字符串。可以在浏览器上显示出返回的字符串。下面我们就可以用这三行代码实现flask所有的功能。
访问情况:
如果没有返回内容,那么会报错500服务器异常:
Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
我们的视图函数没有返回值,那么就是默认返回的None,即使我们给函数后面添加一个return None,结果没有什么变化,这是因为flask返回值是有限制
3.Flask 中的 Response
访问如下服务
可以看到返回的是字符串
响应头中的内容类型是文本。这个内容类型决定了浏览器使用的渲染方式。
而这里表示这是个文本内容,要用html的格式显示出来。这些标签不是我们返回的,我们只是返回下面显示的字符串。这是浏览器读取到了内容类型,帮我们将字符串加了这些标签。
1.
HTTPResponse("hello") "" str
2.render_template
render 响应模板 render_template("html") str
Django中是template。这里因为创建多个模板文件,我们在程序当前目录创建一个templates目录。
并且导入render_template,让函数返回render_template("模板文件名字"),这样当客户端访问这个路由的时候,就能访问到这个模板渲染出来的信息了。
from flask import Flask,render_template
app=Flask(__name__)
@app.route("/index")
def home():
return render_template("index.html")
app.run()
名字写错了报错:
前端文件如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
我是mcw
</body>
</html>
访问情况如下,响应头内容类型中显示内容类型是文本:
我们可以看到模板文件深黄色的,这是编辑器层面也就是pycharm给我们添加的可能出现的错误的提醒。这都是编辑器查找不到所出现的错误。这是pycharm并不知道我们的模板目录,我们可以去掉这个颜色。我们可以将这个目录标记为模板目录
是否选择一个支持的语言,
这里有Django的template语言。我们要选的是Jinja2语言
然后可以看到,模板语言的深黄色标记已经消失
3.redirect("/")
redirect("/") str 以上是Web框架的 Response 三剑客
from flask import Flask,render_template,redirect
app=Flask(__name__)
@app.route("/index")
def home():
return render_template("index.html")
@app.route("/login")
def login():
return redirect("/index")
app.run()
我访问login的时候,跳转到index了
查看login详情,可以看到响应头中添加了location,重定向的地址。响应头中包含location,就会跳转。而redirect所做的事情就是在响应头中添加了location
4.send_file()
from flask import Flask,render_template,redirect,send_file app=Flask(__name__) @app.route("/index") def home(): return render_template("index.html") @app.route("/login") def login(): return redirect("/index") @app.route("/get_file") def get_file(): return send_file("git-cmd.exe") app.run()
instance 返回文件内容,自动识别文件类型,Content-type中添加文件类型,Content-type:文件类型 ** 浏览器特性 可识别的Content-type 自动渲染 不可识别的Content-type 会自动下载
我们导入send_file,
from flask import Flask,render_template,redirect,send_file
app=Flask(__name__)
@app.route("/index")
def home():
return render_template("index.html")
@app.route("/login")
def login():
return redirect("/index")
@app.route("/get_file")
def get_file():
return send_file("app.py")
app.run()
访问这个url,可以看到,把这个文件保留原格式的显示在浏览器上了
我们可以看到响应头的类型中是文本格式,
它给我们添加了html标签显示。text/plain就是要保留当前文件格式的
那么能不能返回二进制文件呢?这里有张照片
我们返回这个照片
然后访问,可以看到可以访问到这个照片了。这是flask给响应头设置了内容格式。返回文件内容,自动识别文件类型,Content-type中添加文件类型
当放的是个mp3的时侯
浏览器上访问,可以发现黑底,图标,然后播放。可以点击暂停。这是流媒体,它不会一次就加载完,分很多次给你加载
详情里可以看它的内容类型和内容长度。电影也是这样,分很多次加载。audio是音频文件,mp格式的,mpeg就是保护mp3,4等一系列mp开头的。
查看渲染的标签是啥,这里是video,实际上我们的是audio,那么为什么用video渲染呢
下面我们发送一个mp4的视频
然后访问查看,这里显示的就是video了
查看渲染的还是video,这是因为无论是视频还是音频,无法区分的太多,浏览器就拿出错几率最小的来渲染,用video 视频音频都可以渲染
假如我们修改为发送一个Windows可执行文件,
那么浏览器不能认识,就会让你下载。x可执行的 ,ms 微软,下载
这体现一个浏览器特性
** 浏览器特性 可识别的Content-type 自动渲染 不可识别的Content-type 会自动下载
5.jsonify() str
# 返回标准格式的JSON字符串 先序列化JSON的字典,Content-type中加入 Application/json
** Flask 1.1.1 版本中 可以直接返回字典格式,无需jsonify
写个函数,导入jsonify。快捷导入模块,ALT+enter键显示出导入啥模块,查看正确就按enter键确认导入,就能快速导入模块
然后它默认在结尾加上了
如下写
访问情况如下:可以看到响应头内容类型是json,并且有数据长度
如果我们返回的是个字典
那么它会将字典返回,并且在浏览器上显示出来。如果没有序列化正常来说是不能返回,但是flask 1.1.1是可以返回字典的,返回的也是application/json格式,实际上它也是使用的jsonify实现的。
如下可以看出来可以直接return的,其中包含字典,实际上它还是会转成字符串,这是1.1.1版本
jsonify() str # 返回标准格式的JSON字符串 先序列化JSON的字典,Content-type中加入 Application/json
** Flask 1.1.1 版本中 可以直接返回字典格式,无需jsonify
一般写api的时候要用到
4.Flask 中的 请求 Request
from flask import Flask,request app=Flask(__name__) @app.route("/login") def login(): #优先判断 请求方式 #如果是GET请求,返回登录页 #如果是POST请求 获取用户密码 校验 if __name__ == '__main__': app.run()
写一个登陆函数写上导入时不执行run,flask中request是一个公共变量,导入这个变量。
我们可以找到这个变量,这是个公共变量,小写,常量一般是大写。公共变量也就是request是可以被多个线程修改的。request中有个管理机制叫请求上下文管理
request.method 获取请求方式
现在函数如下:如果请求方式是get,那么返回登录也面
from flask import Flask,request,render_template
app=Flask(__name__)
@app.route("/login")
def login():
#优先判断 请求方式
if request.method == "GET":
#如果是GET请求,返回登录页
return render_template("login.html")
#如果是POST请求 获取用户密码 校验
if __name__ == '__main__':
app.run()
登录页面如下:添加了登录的form表单
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>魔降风云变</title> </head> <body> <form action="" method="post"> <input type="text" name="username"> <input type="submit" value="登录"> </form> </body> </html>
访问情况如下:
我输入内容点击登录如下,显示方式不被允许。405错误码就是请求方式不被允许,也就是当前的视图函数不允许post提交
后台报错:127.0.0.1 - - [03/Feb/2022 11:12:14] "POST /login HTTP/1.1" 405 -
我们在路由上面添加参数methods,写上支持的请求方式包括get和post,这里写上之后,不是追加,而是覆盖 ,所以要添加上get,这里是可迭代对象列表或者元组,然后打印一下form
然后我们再访问提交
成功打印出来前端提交过来的数据了
提交过来数据是个字典,我们可以让它直接转化成字典,可以如下to_dict()操作
也可以用如下方式直接取字典中的值
如果用中括号取索引,当索引未取到时会报错,所以尽量用get取索引
产生 HTTP 400 错误的原因有:
- 1、前端提交数据的字段名称或者是字段类型和后台的实体类不一致,导致无法封装;
- 2、前端提交的到后台的数据应该是 json 字符串类型,而前端没有将对象转化为字符串类型;
@app.route("/login",methods=["GET","POST"]) def login(): #优先判断 请求方式 #如果是GET请求,返回登录页 if request.method == "GET": return render_template("login.html") #如果是POST请求 获取用户密码 校验 else: print("to_dict",request.form.to_dict()) if request.form.get("username") == "mcw": return "mcw login suceess" return "200 OK"
上面就可以对获取到的用户做校验,校验通过就让它成功登陆
request中还有别的请求方式。在传递url的时候,我们可以使用url进行传参,那么我们如果获取url中的传参呢,args。如下,我们在url中拼接id=1的参数进行访问
后端程序获取url传参,这个传参也可以使用转化到字典的方法。url中除了可以传递参数,还可以传递文件,使用的是base64。
** request.values 获取 url 和 FormData 中的数据 这个里面有个坑,尽量不用,就是url中的参数会覆盖掉FormData中的数据
当我们访问如下地址的时侯
可以看request中的打印信息:
request.path 请求路径 路由地址 /login
request.url 访问请求的完整路径包括 url参数
request.host 主机位 127.0.0.1:5000 ip+端口
request.cookies 字典获取浏览器请求时带上的Cookie
下面看request.files,前端添加数据类型,添加file类型的标签,标签名称为my_file
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>魔降风云变</title> </head> <body> <form action="" method="post"> <input type="text" name="username" formenctype="multipart/form-data"> <input type="file" name="my_file"> <input type="submit" value="登录"> </form> </body> </html>
程序打印一下,files
访问情况如下:
我们上传个文件,点击登录
显示没有登录成功
打印出来是这样的
刚刚位置写错了,重新来:
修改正确之后,就能获取到上传文件的信息了,有一个文件存储对象,是我们上传的图片
下面我们修改重启上传点击登录,可以看到获取到上传的文件存储对象了
下面我们操作这个文件存储对象,将这个文件保存到服务端
上传
查看结果,可以看到已经使用上传文件的文件名为名称,将文件保存到服务端了了。
一般上传上来我们给它重新命令,比如上传上来一个头像,我们这里创建一个头像目录叫avatar。那么我们首先要找到这个目录,然后我们将文件名和目录拼接在一起,最后是save
这样就成功上传了文件
不只是响应当中有content type ,请求当中也有内容类型。如下,enctype
request.json 请求中 Content-Type:application/json 请求体中的数据 被序列化到 request.json 中 以字典的形式存放
request.data 请求中 Content-Type 中不包含 Form 或 FormData 保留请求体中的原始数据 b"" 即Byte。请求的本质上是一个socket传输,传输不能直接传字符串,得用bytes,这里就保留流的原始信息,data一般用的少,比如前端写错了,比如类型不识别就保存原始数据到data里面
总结:
request.form 获取FormData中的数据 也就是所谓的Form标签 to_dict()
request.args 获取URL中的数据 to_dict()
request.json 请求中 Content-Type:application/json 请求体中的数据 被序列化到 request.json 中 以字典的形式存放
request.data 请求中 Content-Type 中不包含 Form 或 FormData 保留请求体中的原始数据 b""
request.files 获取Form中的文件
request.path 请求路径 路由地址
request.url 访问请求的完整路径包括 url参数
request.host 主机位 127.0.0.1:5000
request.cookies 字典获取浏览器请求时带上的Cookie
** request.values 获取 url 和 FormData 中的数据 敏感地带
5.Jinja2
---- template语言
{{ }} 引用 或 执行
{% %} 逻辑引用
1、如下,三个数据结构的数据。初始的程序。
2、这里app.config["DEBUG"]=True和app.debug=True都是为了实现修改程序之后,无需重启程序,自动生效。当客户端访问过来就访问的是最新的程序
3、app.run("0.0.0.0",9527) 指定任意地址都能访问,监听端口为9527,即服务端口
单个字典
STUDENT = {'name': 'mcw01', 'age': 38, 'gender': '中'},
STUDENT_LIST = [
{'name': 'mcw01', 'age': 38, 'gender': '中'},
{'name': 'mcw02', 'age': 73, 'gender': '男'},
{'name': 'mcw03', 'age': 84, 'gender': '女'}
]
STUDENT_DICT = {
1: {'name': 'mcw01', 'age': 38, 'gender': '中'},
2: {'name': 'mcw02', 'age': 73, 'gender': '男'},
3: {'name': 'mcw03', 'age': 84, 'gender': '女'},
}
from flask import Flask
app=Flask(__name__)
app.config["DEBUG"]=True
app.debug=True
if __name__ == '__main__':
app.run("0.0.0.0",9527)
由于起名字问题,用jinja2了,结果执行这个文件时把源码中所有从jinja2导入的变成从自己新建文件导入,导致很多导入出错问题。我把新建文件改了名字为模板学习,所有从jinja2导入的变成草丛模板学习导入的了,虽然把文件名改回来了,但是还有问题的
后来把源码改回去了,果然是因为本地起名冲突,导致从本地导入,结果出现问题了
下面还有很多行出问题,修改一行我就继续让它出现下一个,再修改,后面就好了
如下,传递一个字典进模板,这里用的是关键字传参
STUDENT = {'name': 'mcw01', 'age': 38, 'gender': '中'}, STUDENT_LIST = [ {'name': 'mcw01', 'age': 38, 'gender': '中'}, {'name': 'mcw02', 'age': 73, 'gender': '男'}, {'name': 'mcw03', 'age': 84, 'gender': '女'} ] STUDENT_DICT = { 1: {'name': 'mcw01', 'age': 38, 'gender': '中'}, 2: {'name': 'mcw02', 'age': 73, 'gender': '男'}, 3: {'name': 'mcw03', 'age': 84, 'gender': '女'}, } from flask import Flask,render_template app=Flask(__name__) # app.config["DEBUG"]=True app.debug=True @app.route("/") def home(): return render_template("stu.html",stu=STUDENT) if __name__ == '__main__': app.run("0.0.0.0",9527)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>魔降风云变</title> </head> <body> {{ stu }} </body> </html>
然后在模板里引用
当浏览器访问的时候,显示的是字典被包在元组当中,为啥有个元组呢,跟预期的不符合,是因为传递的字典多了个逗号,将逗号去掉就好了,前端展示的是字典才是正常的
修改如下:取值可以点取,中括号取,或者get取stu中被传递过来的字典中的数据
以上就是单个字典的使用
列表中每个元素都是字典
下面就是列表中多个字典的使用,需要使用for循环
使用for循环
将列表以关键字参数传递进去
我不想要中,当不是男不是女的时候想要它是女
这样就要添加判断语句,当这个值不是男并且不是女的时候,显示女,否则显示当前值
这样就正常显示性别了
字典中每个键值对的值都是字典1
将它传进去
循环每个字典的键,字典加键获取内层字典,然后使用内层字典的数据
结果:
字典中每个键值对的值都是字典2
这次不是循环字典的键,而是循环字典的items,items后面记得加括号。也可只拿键或者值,keys(),values()
结果:
传递自定义函数到模板中,以及将自定义函数作为所有模板共用的函数
自定义一个函数,定义一个视图函数,在视图函数中将函数传递到模板文件中
在模板中使用这个函数,
那么函数的执行结果会渲染在这个模板中,访问结果如下
下面将这个ab函数设置为模板共用的
只需将函数前面加个装饰器,就使它变成全局的模板了,并且不需要传递ab这个函数,在模板中就能调用这个函数。否则,如果有100个视图函数,那么100个视图函数都要传递一次ab函数
访问结果:
宏指令(几乎不用)
前端模板文件中生成宏指令,然后在模板中传参使用,如下栗子:
模板文件中生成宏指令,后面再传参调用,就生成想要的标签,这个直接传递函数应该也可以,更好吧。
访问可以看到生成的标签
后端生成html传递到模板文件中
前端调用一下
可以看到它是字符串,这时需要将它转换成标签显示
前端使用safe就可以实现,如果在不能改动前端的基础上实现将传递的字符串过去,作为标签显示出来,那么...
不修改前端添加safe的话,那么给传递的标签字符串改为markup字符串,即将字符串用Markup包起来
后端生成前端html组件的函数
生成的组件,我们可以变成全局,然后每个模板中调用函数,或者是视图函数中将这个函数传递进模板使用
STUDENT = {'name': 'mcw01', 'age': 38, 'gender': '中'} STUDENT_LIST = [ {'name': 'mcw01', 'age': 38, 'gender': '中'}, {'name': 'mcw02', 'age': 73, 'gender': '男'}, {'name': 'mcw03', 'age': 84, 'gender': '女'} ] STUDENT_DICT = { 1: {'name': 'mcw01', 'age': 38, 'gender': '中'}, 2: {'name': 'mcw02', 'age': 73, 'gender': '男'}, 3: {'name': 'mcw03', 'age': 84, 'gender': '女'}, } from flask import Flask,render_template,Markup,session,sessions app=Flask(__name__) # app.config["DEBUG"]=True app.debug=True @app.route("/") def home(): print(STUDENT) return render_template("stu.html",stu=STUDENT,stu_l=STUDENT_LIST,stu_d=STUDENT_DICT) @app.template_global() def ab(a,b): return a+b @app.template_global() def my_input(na,ty): s=f"<input type='{ty}' value='{na}'>" return Markup(s) @app.route("/a") def homea(): inp=Markup("<input type='submit' value='后端给返回的按钮'>") return render_template("my_a.html",btn=inp) if __name__ == '__main__': app.run("0.0.0.0",9527)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>魔降风云变</title> </head> <body> {{ ab(4,2) }} {{ stu }}<br> 列表中每个值都是字典: <table BORDER="1" cellspacing="0"> <tr> <td>name</td> <td>age</td> <td>gender</td> </tr> {% for student in stu_l %} <tr> <td>{{ student.name }}</td> <td>{{ student["age"] }}</td> <td> {% if student.get("gender") !="男" and student.get("gender") !="女" %} 女 {% else %} {{ student.get("gender") }} {% endif %} </td> </tr> {% endfor %} </table> 字典中的每个值都是字典: <table BORDER="1" cellspacing="0"> <tr> <td>键</td> <td>name</td> <td>age</td> <td>gender</td> </tr> {# {% for skey in stu_d %}#} {# <tr>#} {# <td>{{ stu_d.get(skey).name }}</td>#} {# <td>{{ stu_d.get(skey)["age"] }}</td>#} {# <td>#} {# {% if stu_d.get(skey).get("gender") !="男" and stu_d.get(skey).get("gender") !="女" %}#} {# 女#} {# {% else %}#} {# {{ stu_d.get(skey).get("gender") }}#} {# {% endif %}#} {# </td>#} {# </tr>#} {# {% endfor %}#} {% for skey,svlue in stu_d.items() %} <tr> <td>{{ skey }}</td> <td>{{ svlue.name }}</td> <td>{{ svlue["age"] }}</td> <td> {% if svlue.get("gender") !="男" and svlue.get("gender") !="女" %} 女 {% else %} {{ svlue.get("gender") }} {% endif %} </td> </tr> {% endfor %} </table> </body> </html>
Flask中的session
简单介绍
session和request在这里,除了session和request不一样,其它都是一样的。
这里的session基于请求上下文机制
当导入session的时候,可以看到有个sessions,这是个文件,session都是基于这个文件来的
使用简单栗子
先写一个登录函数
@app.route("/login",methods=["GET","POST"]) def login(): if request.method == "GET": return render_template("login.html") else: user_info=request.form.to_dict() print(user_info) return "login OK!"
可以正常访问
现在我们需要的不是打印userinfo,而是获取到用户名然后存进session中
获取到用户名并保存进session中,但是报错了
RuntimeError: The session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret.
为什么后端也能看到报错信息呢,在这里是因为debug现在设置的true,所以会把后端报错映射到前端展示出来
翻译一下。我们的flask应用承载在workzauk上,workzauk是个wsgi,wsgi收到请求之后把请求序列化了,然后扔给我们的应用,我们的应用就是flask实例。也就是我们的app就是我们的application。
那么我们就在应用上生成一个secret_key。这里随便写的
app.secret_key="$@IIO#JD)@(&@^#$NFNAJOOK;k%@#)FJO(@*$&#*ccsia86"
我们再次请求试试
在session中获取到值,也就是我们成功的把前端提交的数据保存到session中了
session保存机制以及使用
Django的session是存到数据库里了,那么flask的session是存到哪里了呢。session的生命周期,这里默认是31天。如果把session中获取的数据放到get方式下,当get请求时,也是每次请求都会打印出这个session的内容。即使把pycharm关掉重新打开,然后访问还是可以打印出来。
我们可以看到session保存到浏览器上面了。在应用的cookies下可以看到
当我们把它删除掉之后,
发送一个get请求,可以发现获取的值是None了,之前获取的是mcw,正常的数据。当我们重新输入名字,点击登录,就会重新产生session又能重新获取到了。
现在浏览器上重新登录后,有了新的session了
我在secret_key开头加个1。
然后再次访问,可以看到获取到的是None
当把新增的1去掉后,重新访问
发现又有了。这是因为密钥被修改了,原来加密的用被修改过的密钥配不上,
session登录状态,单个函数使用
如下,现在/a的页面如下:
/login页面如下:
我需要实现当访问/a页面时,如果登录了,就正常访问;如果没有登录,那么返回登录页进行登录
STUDENT = {'name': 'mcw01', 'age': 38, 'gender': '中'} STUDENT_LIST = [ {'name': 'mcw01', 'age': 38, 'gender': '中'}, {'name': 'mcw02', 'age': 73, 'gender': '男'}, {'name': 'mcw03', 'age': 84, 'gender': '女'} ] STUDENT_DICT = { 1: {'name': 'mcw01', 'age': 38, 'gender': '中'}, 2: {'name': 'mcw02', 'age': 73, 'gender': '男'}, 3: {'name': 'mcw03', 'age': 84, 'gender': '女'}, } from flask import Flask, render_template, Markup, session, sessions, request, redirect app=Flask(__name__) # app.config["DEBUG"]=True app.debug=True app.secret_key="$@IIO#JD)@(&@^#$NFNAJOOK;k%@#)FJO(@*$&#*ccsia86" @app.route("/") def home(): print(STUDENT) return render_template("stu.html",stu=STUDENT,stu_l=STUDENT_LIST,stu_d=STUDENT_DICT) @app.template_global() def ab(a,b): return a+b @app.template_global() def my_input(na,ty): s=f"<input type='{ty}' value='{na}'>" return Markup(s) @app.route("/a") def homea(): #校验用户登录状态? #校验session中有没有user Key if session.get("user"): inp=Markup("<input type='submit' value='后端给返回的按钮'>") return render_template("my_a.html",btn=inp) #校验失败,跳转login else: return redirect("/login") @app.route("/login",methods=["GET","POST"]) def login(): if request.method == "GET": print(session.get("user")) return render_template("login.html") else: user_info=request.form.to_dict() session["user"]=user_info.get("username") print(session.get("user")) return "login OK!" if __name__ == '__main__': app.run("0.0.0.0",9527)
现在能正常访问,我把session删掉重新访问
删除session之后,访问/a就302跳转到/login了,符合预期
session多个页面使用,多层装饰器使用
虽然/a实现了登录才能访问,但是/首页却是没有实现,如果每个视图函数都写一遍,显然是不可能。
既然不能每个视图函数都写,那么就用装饰器实现,这里先写一个装饰器
多层装饰器的执行顺序是自下而上执行的。我们需要看这个最终装饰的函数是谁,再去做装饰器的叠加。war装饰之后返回的是inner,inner就是home,home需要被route装饰。route在war上面,那么war返回的home就被route装饰,如果没有route装饰,那么肯定就无法响应数据。比如war放到route上了,那么route先给响应了,还没有到war装饰呢。所以route放到最外层,它要做响应数据。而这里的war我们是为了做session校验用的。
如下,就完成了使用装饰器进行session校验。在每个视图函数前加上这个装饰器
现在我们再次访问/,发现302跳转到登录页了
当我们登录有了session之后
再次访问/,直接访问成功
程序汇总
STUDENT = {'name': 'mcw01', 'age': 38, 'gender': '中'} STUDENT_LIST = [ {'name': 'mcw01', 'age': 38, 'gender': '中'}, {'name': 'mcw02', 'age': 73, 'gender': '男'}, {'name': 'mcw03', 'age': 84, 'gender': '女'} ] STUDENT_DICT = { 1: {'name': 'mcw01', 'age': 38, 'gender': '中'}, 2: {'name': 'mcw02', 'age': 73, 'gender': '男'}, 3: {'name': 'mcw03', 'age': 84, 'gender': '女'}, } from flask import Flask, render_template, Markup, session, sessions, request, redirect app=Flask(__name__) # app.config["DEBUG"]=True app.debug=True app.secret_key="$@IIO#JD)@(&@^#$NFNAJOOK;k%@#)FJO(@*$&#*ccsia86" def war(func): def inner(*args,**kwargs): #校验session if session.get("user"): ret=func(*args,**kwargs) #func=home return ret else: return redirect("/login") return inner @app.route("/") @war def home(): print(STUDENT) return render_template("stu.html",stu=STUDENT,stu_l=STUDENT_LIST,stu_d=STUDENT_DICT) @app.template_global() def ab(a,b): return a+b @app.template_global() def my_input(na,ty): s=f"<input type='{ty}' value='{na}'>" return Markup(s) @app.route("/a") def homea(): #校验用户登录状态? #校验session中有没有user Key if session.get("user"): inp=Markup("<input type='submit' value='后端给返回的按钮'>") return render_template("my_a.html",btn=inp) #校验失败,跳转login else: return redirect("/login") @app.route("/login",methods=["GET","POST"]) def login(): if request.method == "GET": print(session.get("user")) return render_template("login.html") else: user_info=request.form.to_dict() session["user"]=user_info.get("username") print(session.get("user")) return "login OK!" if __name__ == '__main__': app.run("0.0.0.0",9527)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 我是mcw </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>魔降风云变</title> </head> <body> <form action="" method="post" enctype="multipart/form-data"> <input type="text" name="username" > <input type="file" name="my_file"> <input type="submit" value="登录"> </form> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ ab(1,2) }} {% macro my_input(na,ty) %} <input type="{{ ty }}" name="{{ na }}"> {% endmacro %} {{ my_input("username","password") }} {{ btn }} </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>魔降风云变</title> </head> <body> {{ ab(4,2) }} {{ stu }}<br> 列表中每个值都是字典: <table BORDER="1" cellspacing="0"> <tr> <td>name</td> <td>age</td> <td>gender</td> </tr> {% for student in stu_l %} <tr> <td>{{ student.name }}</td> <td>{{ student["age"] }}</td> <td> {% if student.get("gender") !="男" and student.get("gender") !="女" %} 女 {% else %} {{ student.get("gender") }} {% endif %} </td> </tr> {% endfor %} </table> 字典中的每个值都是字典: <table BORDER="1" cellspacing="0"> <tr> <td>键</td> <td>name</td> <td>age</td> <td>gender</td> </tr> {# {% for skey in stu_d %}#} {# <tr>#} {# <td>{{ stu_d.get(skey).name }}</td>#} {# <td>{{ stu_d.get(skey)["age"] }}</td>#} {# <td>#} {# {% if stu_d.get(skey).get("gender") !="男" and stu_d.get(skey).get("gender") !="女" %}#} {# 女#} {# {% else %}#} {# {{ stu_d.get(skey).get("gender") }}#} {# {% endif %}#} {# </td>#} {# </tr>#} {# {% endfor %}#} {% for skey,svlue in stu_d.items() %} <tr> <td>{{ skey }}</td> <td>{{ svlue.name }}</td> <td>{{ svlue["age"] }}</td> <td> {% if svlue.get("gender") !="男" and svlue.get("gender") !="女" %} 女 {% else %} {{ svlue.get("gender") }} {% endif %} </td> </tr> {% endfor %} </table> </body> </html>
import os from flask import Flask,request,render_template app=Flask(__name__) @app.route("/login",methods=["GET","POST"]) def login(): #优先判断 请求方式 #如果是GET请求,返回登录页 if request.method == "GET": return render_template("login.html") #如果是POST请求 获取用户密码 校验 else: my_file=request.files.get("my_file") filename=my_file.filename #获取原始文件名 file_path=os.path.join("avatar",filename) my_file.save(file_path) if request.form.get("username") == "mcw": return "mcw login suceess" return "login not OK" if __name__ == '__main__': app.run()