Flask
Python 现阶段三大主流Web框架 Django Tornado Flask 对比:
Django 主要特点是大而全,集成了很多组件,例如: Models Admin Form 等等, 不管你用得到用不到,反正它全都有,属于全能型框架
Flask 主要特点小而轻,原生组件几乎为0, 三方提供的组件请参考Django 非常全面,属于短小精悍型框架
Tornado 优点是异步,用于游戏服务后台,缺点是干净,连个Session都不支持
flask和django最大的不同点:request/session 是需要单独导入的
flask知识点:
- 模板+静态文件,app= Flask(__name__,....) - 路由 @app.route('/index',methods=["GET"]) - 请求 request.form request.args request.method -响应 render redirect -session session['xx'] = 123 session.get('xx')
Flask初级实现:
from flask import Flask #Flask是个类 #传参实例化Flask,拿到一个对象app app = Flask(__name__) @app.route('/index') #路由和下面函数绑定,@后面写上面实例化对象的名字 #写一个函数,return一个结果 def index(): return "hello word-flask" #调用里面的run方法 app.run()
优化为:
from flask import Flask #Flask是个类 #传参实例化Flask,拿到一个对象app app = Flask(__name__) @app.route('/index') #路由和下面函数绑定,@后面写上面实例化对象的名字 #写一个函数,return一个结果 def index(): return "hello word-flask" #调用里面的run方法 if __name__ == '__main__': app.run()
知识点一:用到的知识点有rsplit::切分 、getattr:返回对象属性值 、dir:返回模块的属性列表
通过给定一个字符串路径,找到对应的类及类里面的属性和属性值
例如:- 给你一个路径 “settings.Foo”,可以找到类并获取去其中的大写的静态字段。
test.py里面的设置
import importlib path = 'settings.Foo' p,c =path.rsplit('.',maxsplit=2) #maxsplit代表切分几次 # print(p,c) #settings Foo #拿到settings所在的路径 m=importlib.import_module(p) #<module 'settings' from 'E:\\知识点练习\\知识点回忆\\settings.py'> #getattr返回对象属性值 cls=getattr(m,c) print(cls) #<class 'settings.Foo'> """例如下面: >>>class A(object): ... bar = 1 ... >>> a = A() >>> getattr(a, 'bar') # 获取属性 bar 值 """ #如何找到这个类 #dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。 # print(dir(cls)) #['DEBUG', '__class__', '__delattr__',...] for key in dir(cls): if key.isupper(): #DEBUG True 最终拿到类中属性及属性值 print(key,getattr(cls,key))
配置文件:查看、修改
settings.py设置:设置配置文件参数
#通过settings文件,完成对app.config配置文件的修改 #分类,方便app里面不同的应用场景调用 #共同需要的配置写在基类里面,下面单独修改的部分去继承这个基类 class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' # 开发环境 class DevelopmentConfig(Config): DEBUG = True #上线环境 class ProductionConfig(Config): DATABASE_URI = 'mysql://user@localhost/foo' class TestingConfig(Config): TESTING = True
app.py配置:引用settings配置文件,实现查看、修改
from flask import Flask,render_template,redirect,request app=Flask(__name__) #查看配置文件 print(app.config) #修改配置文件 #方式一:单独修改一项 # app.config['DEBUG'] = True #方式二:批量修改,from_object后面是个字符串(根据字符串找到settings类,在类里面统一去做一些修改) app.config.from_object("settings.DevelopmentConfig") #根据需求调用settings配置文件里面的类 print(app.config) if __name__ == '__main__': app.run()
路由系统:
首先,要导入url_for: from flask import url_for
近而,通过url_for反向生成url地址
静态路由:无参数、反向解析endpoint
@app.route('/index/123/456',methods=['GET','POST'],endpoint='n1') #endpoint='n1'类似于Django里面的name,用于反向解析 def index(): # 通过url_for 反向生成url print(url_for('n1')) #/index/123/456 # 如果不指定endpoint='n1',默认写路由对应装饰的函数的名字 print(url_for('index')) #/index/123/456 user=session.get('user') # print(session) session类似个字典(里面包含的用户名信息): <SecureCookieSession {'user': 'yzz'}> if not user: return redirect('/login') return render_template('index.html')
动态路由:有参数、也可以搭配反向解析使用
#动态路由(<int:nid>)index要接收nid @app.route('/index/<int:nid>', methods=['GET', 'POST']) # endpoint='n1'类似于Django里面的name,用于反向解析 def index(nid): print(nid) #拿到的时你实际浏览器输入的值 #url_for 有参数就传参数,没参数就写endpoint的值 print(url_for('index',nid=733)) #/index/733 user=session.get('user') # print(session) session类似个字典(里面包含的用户名信息):<SecureCookieSession {'user': 'yzz'}> if not user: return redirect('/login') return render_template('index.html')
FBV:
请求与响应:
#请求与响应 # 请求相关信息 request.method request.args request.form request.values request.cookies request.headers request.path request.full_path request.script_root request.url request.base_url request.url_root request.host_url request.host request.files obj = request.files['the_file_name'] obj.save('/var/www/uploads/' + secure_filename(f.filename)) # 响应: # 响应体: 第一种:return “asdf” 第二种:return jsonify({'k1': 'v1'}) #jsonify将字典转为字符串返回 第三种:return render_template('xxx.html') 第四种:return redirect() # 定制响应头:例如第一种方式 obj = make_response("asdf") #设置个响应体 obj.headers['xxxxxxx'] = '123' #设置个响应头 响应头里面会增加一项:xxxxxxx:123 obj.set_cookie('key', 'value') #设置cookies 响应头里面会增加一项:Set-Cookie:key=value; Path=/ return obj
实例:
版本一:学生管理-简单登录、删除、查看
from flask import Flask #导入三大组件:render_template模板渲染 redirect重定向 from flask import render_template,request,redirect,session,url_for,jsonify,make_response app = Flask(__name__,template_folder="templates",static_folder="static") #默认后台文件夹的名字 #需要加盐(因为flask的session是放在本地cookie里面的,只不过是加密),也可以添加在app.settings配置里面 app.secret_key = 'fjsjflks' # 修改配置文件 app.config.from_object("settings.DevelopmentConfig") #根据需求调用settings配置文件里面的类 STUDENT_DICT = { 1:{'name':'张三','age':12,'gender':'男'}, 2:{'name': '李四', 'age': 34, 'gender': '女'}, 3:{'name': '王五', 'age': 45, 'gender': '男'}, } #路由配置 @app.route('/login',methods=['GET','POST']) def login(): if request.method=="GET": # return 'login' return render_template('login.html') user = request.form.get('user') pwd = request.form.get('pwd') if user == 'yzz' and pwd == '123': session['user'] = user return redirect('/index') return render_template('login.html', error='用户名或密码错误') #静态路由(无参数) @app.route('/index') def index(): user = session.get('user') if not user: return redirect('/login') return render_template('index.html',stu_dic=STUDENT_DICT) @app.route('/delete/<int:nid>') #接收前端点击,对应的nid,战队nid进行删除 def delete(nid): user = session.get('user') if not user: # return redirect('/login') return redirect(url_for('login')) del STUDENT_DICT[nid] return redirect(url_for('index')) @app.route('/detail/<int:nid>') def detail(nid): user = session.get('user') if not user: return redirect('/login') info = STUDENT_DICT[nid] return render_template('detail.html',info=info) if __name__ == '__main__': app.run()
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>用户登录</h1> <form method="post"> <input type="text" name="user"> <input type="password" name="pwd"> <input type="submit" value="提交">{{error}} </form> </body> </html>
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>学生列表</h1> <table border="1px"> <thead> <tr> <th>ID</th> <th>姓名</th> <th>年龄</th> <th>性别</th> <th>选项</th> </tr> </thead> <tbody> {% for k,v in stu_dic.items()%} <tr> <td>{{k}}</td> <td>{{v.name}}</td> <td>{{v.age}}</td> <td>{{v.gender}}</td> <td> <a href="/detail/{{k}}">查看详情</a> | <a href="/delete/{{k}}">删除</a> </td> </tr> {%endfor%} </tbody> </table> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>学生详情</title> </head> <body> {% for item in info.values()%} <li>{{item}}</li> {%endfor%} </body> </html>
版本二:认证装饰器-基于functools模块(应用于比较少的函数中需要额外添加功能)
from flask import Flask #导入三大组件:render_template模板渲染 redirect重定向 from flask import render_template,request,redirect,session,url_for,jsonify,make_response app = Flask(__name__,template_folder="templates",static_folder="static") #默认后台文件夹的名字 #需要加盐(因为flask的session是放在本地cookie里面的,只不过是加密),也可以添加在app.settings配置里面 app.secret_key = 'fjsjflks' # 修改配置文件 app.config.from_object("settings.DevelopmentConfig") #根据需求调用settings配置文件里面的类 STUDENT_DICT = { 1:{'name':'张三','age':12,'gender':'男'}, 2:{'name': '李四', 'age': 34, 'gender': '女'}, 3:{'name': '王五', 'age': 45, 'gender': '男'}, } #登录认证装饰器 import functools def auth(func): @functools.wraps(func) def innder(*args,**kwargs): if not session.get('user'): return redirect(url_for('login')) ret = func(*args,**kwargs) return ret return innder #路由配置 @app.route('/login',methods=['GET','POST']) def login(): if request.method=="GET": # return 'login' return render_template('login.html') user = request.form.get('user') pwd = request.form.get('pwd') if user == 'yzz' and pwd == '123': session['user'] = user return redirect('/index') return render_template('login.html', error='用户名或密码错误') #静态路由(无参数) @app.route('/index') @auth def index(): return render_template('index.html',stu_dic=STUDENT_DICT) print(index.__name__) @app.route('/delete/<int:nid>') #接收前端点击,对应的nid,战队nid进行删除 @auth def delete(nid): del STUDENT_DICT[nid] return redirect(url_for('index')) print(delete.__name__) @app.route('/detail/<int:nid>') @auth def detail(nid): info = STUDENT_DICT[nid] return render_template('detail.html',info=info) print(detail.__name__) if __name__ == '__main__': app.run()
版本三:认证装饰器-基于@app.before_request(应用于需要装饰的方法较多时)
@app.before_request def xxxx(): if request.path == '/login': return None #return None,表示允许通过,继续显示相应页面 if session.get('user'): return None return redirect('/login')
from flask import Flask #导入三大组件:render_template模板渲染 redirect重定向 from flask import render_template,request,redirect,session,url_for,jsonify,make_response app = Flask(__name__,template_folder="templates",static_folder="static") #默认后台文件夹的名字 #需要加盐(因为flask的session是放在本地cookie里面的,只不过是加密),也可以添加在app.settings配置里面 app.secret_key = 'fjsjflks' # 修改配置文件 app.config.from_object("settings.DevelopmentConfig") #根据需求调用settings配置文件里面的类 STUDENT_DICT = { 1:{'name':'张三','age':12,'gender':'男'}, 2:{'name': '李四', 'age': 34, 'gender': '女'}, 3:{'name': '王五', 'age': 45, 'gender': '男'}, } @app.before_request def xxxx(): if request.path == '/login': return None #return None,表示允许通过,继续显示相应页面 if session.get('user'): return None return redirect('/login') #路由配置 @app.route('/login',methods=['GET','POST']) def login(): if request.method=="GET": # return 'login' return render_template('login.html') user = request.form.get('user') pwd = request.form.get('pwd') if user == 'yzz' and pwd == '123': session['user'] = user return redirect('/index') return render_template('login.html', error='用户名或密码错误') #静态路由(无参数) @app.route('/index') def index(): return render_template('index.html',stu_dic=STUDENT_DICT) @app.route('/delete/<int:nid>') #接收前端点击,对应的nid,战队nid进行删除 def delete(nid): del STUDENT_DICT[nid] return redirect(url_for('index')) @app.route('/detail/<int:nid>') def detail(nid): info = STUDENT_DICT[nid] return render_template('detail.html',info=info) if __name__ == '__main__': app.run()
Flask中的三剑客:Render_template Redirect HttpResponse:
HttpResponse:就是直接返回字符串
Redirect:网页跳转重定向
Render_template:使用时需要再主目录中加入一个templates 目录(否则会报一个jinjia2...的异常)
Request:
注意点:
1.解释一个 @app.route("/req",methods=["POST"]) :
methods=["POST"] 代表这个url地址只允许 POST 请求,是个列表也就是意味着可以允许多重请求方式,例如GET之类的
2.Form表单中传递过来的值 使用 request.form 中拿到
3.Flask 的 request 中给我们提供了一个 method 属性里面保存的就是前端的请求的方式
4.request.args 与 request.form 的区别就是:
request.args:是获取url中的参数
request.form :是获取form表单中的参数
5.request.values :只要是个参数我都要
6.reuquest.cookies:将cookie信息读取出来
7.request.headers:拿出请求头中的的秘密
8.request.files:拿到的是你上传的文件
9. request.json 之 前提你得告诉是json
如果在请求中写入了 "application/json" 使用 request.json 则返回json解析数据, 否则返回 None
request.files:
html配置: <form method="post" action="" enctype="multipart/form-data"> #注意上传文件要配置enctype <p>用户名<input type="text" name="username"></p> <p>用户名<input type="password" name="password"></p> <p><input type="file" name="file"></p> <p><input type="submit" value="提交"></p> <p> {{ msg }} </p> </form> 打印输出: print(request.files) 验证结果: ImmutableMultiDict([('file', <FileStorage: '011.docx' ('application/vnd.openxmlformats-officedocument.wordprocessingml.document')>)])
上面request其他的验证:
from flask import Flask,render_template,redirect,request app=Flask(__name__) @app.route("/index") def index(): print(request.args) #ImmutableMultiDict([('id', '1'), ('age', '30')]) return "Hello Flask,Welcome you" @app.route("/login",methods=("POST","GET")) def login(): if request.method == "POST": # 它看起来像是的Dict print(request.form) #ImmutableMultiDict([('username', 'yzz'), ('password', 'yzz')]) # print(request.form.keys()) #<dict_keyiterator object at 0x042FF5A0> # print(list(request.form.keys())) #['username', 'password'] username=request.form.get('username') #yzz password=request.form.get('password') if username == 'yzz' and password == 'yzz': return redirect('/index') else: return render_template("template.html",msg="密码错误") return render_template("template.html", msg=None) app.run(debug=True)
模板渲染:
查看学生信息:就向后端传入字典、列表(套字典)、字典(套字典)
from flask import Flask,render_template,redirect,request app=Flask(__name__) #格式一: STUDENT = {'name': 'Old', 'age': 38, 'gender': '中'}, ''' templates设置: return render_template("stu.html",student=STUDENT) 路由函数设置: {{student}} ''' #格式二: STUDENT_LIST = [ {'name': 'Old', 'age': 38, 'gender': '中'}, {'name': 'Boy', 'age': 73, 'gender': '男'}, {'name': 'EDU', 'age': 84, 'gender': '女'} ] ''' templates设置: <table border="1xp"> {% for foo in student %} <tr> <td>{{ foo.name }}</td> <td>{{ foo.get("age") }}</td> <td>{{ foo["gender"] }}</td> </tr> {% endfor %} </table> 路由函数设置: return render_template("stu.html",student=STUDENT_LIST) ''' #格式三: STUDENT_DICT = { 1: {'name': 'Old', 'age': 38, 'gender': '中'}, 2: {'name': 'Boy', 'age': 73, 'gender': '男'}, 3: {'name': 'EDU', 'age': 84, 'gender': '女'}, } ''' templates设置: <table border="1xp"> {% for k,v in student.items() %} <tr> <td>{{ v.name }}</td> <td>{{ v.get("age") }}</td> <td>{{ v["gender"] }}</td> </tr> {% endfor %} </table> 路由函数设置: return render_template("stu.html",student=STUDENT_DICT) '''
return_template也可以传递多个值:
@app.route("/allstudent") def all_student(): return render_template("all_student.html", student=STUDENT , student_list = STUDENT_LIST, student_dict= STUDENT_DICT)
Jinja2中的safe用法(xss攻击有关,如何显示html代码让浏览器识别定执行),结果输出就不是字符串,而是执行后的网页渲染效果
正常网页显示结果:
<input type='text' name='user' value='DragonFire'> #普通字符串格式
为了让浏览器能够识别并执行这个html代码,可以通过以下2中方式实现;
方式一:前端加safe实现
<body> <!--方式一:--> {{ tag | safe }} </body>
方式二;后端导入Markup,实现
from flask import Markup
@app.route("/index") def index(): #方式二: #导入Markup from flask import Markup tag = "<input type='text' name='user' value='DragonFire'>" markup_tag=Markup(tag) print(markup_tag,type(markup_tag)) #<input type='text' name='user' value='DragonFire'> <class 'markupsafe.Markup'> return render_template("index.html", tag=markup_tag)