浅谈 Flask 框架
-
Django —— 教科书式框架
-
优势:组件全,功能全,教科书
-
劣势:占用资源,创建复杂度高
-
-
Flask —— 以简单为基准开发,一切从简,能省则省
-
优势:轻,块
-
劣势:先天不足,第三方组件稳定性较差
-
Django | Flask |
---|---|
Admin —— Model | 原生无 |
Model | 原生无 |
Form | 原生无 |
Session | 有 —— 颠覆认知操作 |
二、Flask入门
-
下载安装
-
下载:pip install Flask
-
注意:不要使用工具中的插件创建 Flask 项目
-
-
三行代码启动Flask项目
from flask import Flask app = Flask(__name__) app.run()
三、Flask的请求和响应
3.1 Flask中的响应Response
-
与django对比
Django Flask 返回 HttpResponse("hello") "hello" 字符串 render(request,"html",{"k":v}) render_template("html",k=v) html页面 redirect("/") redirect("/") 重定向 JsonResponse({"k":v}) jsonify({"k":v}) 标准的json字符串 -
以上是web框架的Response三剑客
-
-
返回字符串 —— string
from flask import Flask app = Flask(__name__) @app.route("/") def home(): return "hello World I am Flask" app.run()
-
返回html页面 —— string
from flask import Flask, render_template app = Flask(__name__) @app.route("/index") def index(): return render_template("index.html") app.run()
-
返回重定向 —— string
from flask import Flask, redirect app = Flask(__name__) @app.route("/login") def login(): return redirect("/index") app.run()
-
返回文件内容 —— Response instance
-
send_file():读取并返回文件内容,自动识别文件类型,Content-type中添加类型,即,Content-type:文本类型
-
浏览器特性:
-
可识别的Content-type,会自动渲染
-
不可识别的Content-type,会自动下载
-
from flask import Flask, send_file app = Flask(__name__) @app.route("/get_file") def get_file(): # 返回文件内容,自动识别文件类型,Content-type中添加文件类型,Content-type:文件类型 return send_file("2.exe") app.run()
-
-
返回标准格式的json字符串 —— string
-
jsonify():返回标准格式的json字符串,本质:先序列化json的字典,Content-type中加入Application/json,Flask1.1.1 直接返回字典格式,相当于自动执行jsonify
from flask import Flask, send_file app = Flask(__name__) @app.route("/get_json") def get_json(): d = {"k":"v"} # 返回标准Json格式字符串 API接口 return jsonify(d) app.run()
-
3.2 Flask中的请求Request
-
与django对比
Django Flask Flask中的含义 request.method request.method 获取请求方式 request.GET request.args 获取URL中的数据 request.POST request.form 获取FormData中的数据 ,也就是所谓的Form标签 request.FILES request.files 获取request中的文件,返回FileStorage中,存在save(保存文件)和filename(原始文件名) request.json 请求中Content-Type:application/json,请求体中的数据被序列化到 request.json中,以字典的形式存放 request.data 请求中Content-Type中不包含Form或FormData,保留请求体中的原始数据,bytes类型 request.path_info request.path 请求路径 路由地址 request.get_full_patch() request.url 访问请求的完整路径包括 url参数 request.get_host() request.host 主机位 127.0.0.1:5000 request.COOKTES request.cookies 字典获取浏览器请求时带上的Cookie -
注意:request.values,获取url和FormData中的数据,敏感地带(url和FormData中存在相同的key时会出错)
-
import os from flask import Flask, request, render_template # request 请求上下文管理 app = Flask(__name__) @app.route("/login",methods=["GET","POST"]) def login(): # print(request.args.to_dict()) # print(request.host) # print(request.path) # print(request.url) # print(request.cookies) # 优先判断 请求方式 # 如果是 GET 请求 返回登录页面 if request.method == "GET": return render_template("login.html") # 如果是 POST 请求 获取用户名密码 校验 else: # 405 请求方式不被允许 my_file = request.files.get("my_file") filename = my_file.filename # 获取原始文件名 file_path = os.path.join("avatar",filename) my_file.save(file_path) print(request.form.to_dict()) # form - FormData if request.form.get("username") == "YWB": return "Login OK!" return "Login 不OK!" if __name__ == '__main__': app.run()
四、 Jinja2:template语言
-
{{ }} 引用或执行
-
{% %} 逻辑引用
STUDENT = {'name': 'Old', 'age': 38, 'gender': '中'} STUDENT_LIST = [ {'name': 'Old', 'age': 38, 'gender': '中'}, {'name': 'Boy', 'age': 73, 'gender': '男'}, {'name': 'EDU', 'age': 84, 'gender': '女'} ] STUDENT_DICT = { 1: {'name': 'Old', 'age': 38, 'gender': '中'}, 2: {'name': 'Boy', 'age': 73, 'gender': '男'}, 3: {'name': 'EDU', 'age': 84, 'gender': '女'}, } from flask import Flask, render_template, Markup app = Flask(__name__) @app.template_global() def ab(a,b): return a+b @app.route("/") def home(): return render_template("stu.html",stu=STUDENT,stu_l = STUDENT_LIST,stu_d = STUDENT_DICT) @app.template_global() def my_input(na,ty): s = f"<input type='{ty}' value='{na}'>" return Markup(s) if __name__ == '__main__': app.run()
<p>{{ ab(50,60) }}</p> {{ stu }} <table border="1px"> <tr> <td>name</td> <td>age</td> <td>gender</td> </tr> <tr> <td>{{ stu.name }}</td> <td>{{ stu["age"] }}</td> <td>{{ stu.get("gender") }}</td> </tr> </table> {{ stu_l }} <table border="1px"> <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> {{ stu_d }} <table border="1px"> <tr> <td>id</td> <td>name</td> <td>age</td> <td>gender</td> </tr> {% for skey,svalue in stu_d.items() %} <tr> <td>{{ skey }}</td> <td>{{ svalue.get("name") }}</td> <td>{{ svalue.get("age") }}</td> <td> 11111 </td> </tr> {% endfor %} </table>
五、Flask的知识点
5.1 Flask中的session
-
基于请求上下文管理机制
STUDENT = {'name': 'Old', 'age': 38, 'gender': '中'} STUDENT_LIST = [ {'name': 'Old', 'age': 38, 'gender': '中'}, {'name': 'Boy', 'age': 73, 'gender': '男'}, {'name': 'EDU', 'age': 84, 'gender': '女'} ] STUDENT_DICT = { 1: {'name': 'Old', 'age': 38, 'gender': '中'}, 2: {'name': 'Boy', 'age': 73, 'gender': '男'}, 3: {'name': 'EDU', 'age': 84, 'gender': '女'}, } from flask import Flask, render_template, request, session, redirect app = Flask(__name__) # app.config["DEBUG"] = True app.debug = True app.secret_key = "!@#$%^(*&^%$#@#$%&*(^$WQ*(^EWET*^EWEU" def war(func): # func = home 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 # war(home) -> inner == == home # 先要经过一次校验 才能执行或不执行视图函数 def home(): # 校验用户登录 session #如果session。get(“user”) 存在 # 如果不存在 返回Login return render_template("stu.html",stu=STUDENT,stu_l = STUDENT_LIST,stu_d = STUDENT_DICT) @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(): # 当请求方式为 GET 的时候 if request.method == "GET": # eyJ1c2VyIjoiYWxleGFuZGVyLmRzYi5saSJ9.XUfYMw.o8oZX2GMC-kjZPWBkyRuUeSMp4M # 交由客户端保管机制 # {"user":"alexander.dsb.li"} # Flask理念 - 一切从简 为服务器减轻压力 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)
5.2 Flask中的路由
-
@app.route()
-
参数:
-
rule:路由地址,例,"/login"
-
methods:允许进入视图函数的请求方式
-
endpoint:mapping ,路由地址和endpoint之间的mapping,路由地址和视图函数之间还有一个mapping,注意,endpoint在同一个app中不能出现重复,默认值是视图函数名
-
defaults:默认路由参数
-
strict_slashes:是否严格遵循路由匹配规则
-
redirect_to:永久重定向 ,308和301
-
-
from flask import Flask,request,url_for # url_for用于对endpoint设置的名称的反向获取url app = Flask(__name__) @app.route("/a",methods=["GET","PoSt","options","abc"],endpoint="end_homea" ,defaults={"count":20},strict_slashes=True,redirect_to="/") def homea(count): # print(url_for("end_homea")) # 如果在蓝图里面,要使用url_for("蓝图标识".endpoint) # 如文件名为app01,则url_for("app01.end_homea") count = request.args.get("count",count) return f"200OK!{count}"
-
动态参数路由
-
可以分页,获取文件,解决分类,解决正则路由问题
from flask import Flask,request,url_for # url_for用于对endpoint设置的名称的反向获取url app = Flask(__name__) @app.route("/get_img/<filename>") def get_img(filename): filepath = os.path.join("img", filename) return send_file(filepath) # 访问地址 : http://127.0.0.1:5000/info/1 @app.route("/info/<int:nid>", methods=["GET", "POST"], endpoint="r_info") def student_info(nid): print(url_for("r_info",nid=2)) # /info/2,反向url时也要加上参数 return f"Hello Old boy {nid}" # Python3.6的新特性 f"{变量名}"
-
5.3 Flask初始化配置
-
常用配置:
-
template_folder:模板文件存放路径
-
static_folder:静态文件存放目录,默认值是static
-
static_url_path:静态文件访问路径,默认值是/ + static_folder
-
from flask import Flask app = Flask(__name__,template_folder="templatess", static_folder="static",static_url_path="/futeng") # static == 你的家 # /static == 回家路 # /futeng == 富腾路
5.4 Flask实例配置
-
Flask实例配置,即app的配置,可以在app.default_config中查看
# 在setting.py中,写配置对应的类 import hashlib,time class DebugConfig(object): DEBUG = True SECRET_KEY = "#$%^&*($#$%^&*%$#$%^&*^%$#$%" PERMANENT_SESSION_LIFETIME = 3600 SESSION_COOKIE_NAME = "I am Not Session" class TestConfig(object): TESTING = True SECRET_KEY = hashlib.md5(f"{time.time()}#$%^&*($#$%^&*%$#$%^&*^%$#$%{time.time()}".encode("utf8")).hexdigest() PERMANENT_SESSION_LIFETIME = 360000 SESSION_COOKIE_NAME = "#$%^&*($#$%^&*%$#$%^&*^%$#$%" # 在可执行文件中,调用 from flask import Flask from setting import DebugConfig,TestConfig app = Flask(__name__) app.config.from_object(DebugConfig) # 或 app.config.from_object(TestConfig)
5.5 Flask的蓝图
- 蓝图:Blueprint,不能被run的Flask实例 不存在Config
# 在可执行文件中,调用 from flask import Flask from users import bp from car_users import car_bp app = Flask(__name__) app.register_blueprint(bp) app.register_blueprint(car_bp)
from flask import Blueprint bp = Blueprint("app01",__name__) @bp.route("/add_user") def add_user(): return "添加用户" @bp.route("/find_user") def find_user(): return "查看用户" @bp.route("/drop_user") def drop_user(): return "删除用户" @bp.route("/up_user") def up_user(): return "修改用户"
from flask import Blueprint car_bp = Blueprint("app02",__name__,url_prefix="/car") # 参数url_prefix是为了区分users.py和car_users.py中的相同路由 # 使用url_prefix后,访问时,要在原来路由前加上url_prefix设置的路径 @car_bp.route("/add_user") def add_user(): return "car添加用户" @car_bp.route("/find_user") def find_user(): return "car查看用户" @car_bp.route("/drop_user") def drop_user(): return "car删除用户" @car_bp.route("/up_user") def up_user(): print("I am vf") return "car修改用户"
5.6 Flask特殊装饰器
-
@app.before_request:在请求进入视图函数之前 做出处理
-
@app.after_request:在响应返回客户端之前,结束视图函数之后
@app.before_request def be1(): print("I am Be1") # return "第一个请求你就过不去" # 5种 @app.before_request def be2(): print("I am Be2") # return "第二个请求过不去了" # 5种 @app.after_request def af1(res): print("I am af1") return res @app.after_request def af2(res): print("I am af2") return res
-
-
be+af 请求生命周期:
-
正常:be1 -> be2 -> vf -> af2 -> af1
-
异常:be1 -> af2 -> af1
-
注意:只要有响应返回 , af全部执行
-
-
-
@app.errorhandler:重定义错误页面
@app.errorhandler(404) # 只能是 4xx,5xx def error404(error_message): print(error_message) # return redirect("https://www.autohome.com.cn/111232130/#pvareaid=33112794r5454") # 5种类型
5.7 Flask的CBV
-
CBV:继承 MethodView 让当前的 class 可以成为视图类
from flask import Flask app = Flask(__name__) class Login(views.MethodView): def get(self,*args,**kwargs): return "I am Get" def post(self,*args,**kwargs): pass app.add_url_rule("/login", endpoint="my_login", # 可以不设置endpoint view_func=Login.as_view(name="loginlogin") # view_func.__name__="loginlogin" )
六、Flask组件:Flask-Session
-
下载:pip install Flask-Session
-
使用:
from flask import Flask from flask_session import Session from redis import Redis app = Flask(__name__) app.config["SESSION_TYPE"] = "redis" app.config["SESSION_REDIS"] = Redis(host="192.168.12.87",port=6379,db=10) # app.session_interface # Flask 利用 session_interface 选择 Session 的存放位置 和 存放机制 # app.default_config Session(app)
七、Flask总结
-
下载:pip install Flask
-
启动:
from flask import Flask app = Flask(__name__) app.run("0.0.0.0",9527) # 监听地址 监听端口
-
response
# Flask 三剑客 return "" # 返回字符串 HTTPResponse return render_template("index.html") # 返回模板 依赖MarkupSafe 默认存放路径:templates return redirect("/login") # 响应头中加入 - location:重定向 # Flask 特殊Response return send_file(文件名称或文件路径+文件名称) # 打开并返回文件内容 自动识别文件类型 响应头 Content-Type:文件类型 return jsonify({k:v}) # 返回标准格式的json字符串 响应头中 Content-Type:application/json # Flask 1.1.1中直接返回dict 本质上是在运行jsonify
-
request
from flask import request request.form # 获取FormData中的数据 to_dict() 类似字典类型 .get() ["key"] KeyError request.args # 获取URL中的数据 to_dict() 类似字典类型 .get() ["key"] KeyError request.json # 请求头中 Content-Type:application/json 存放字典 request.data # 请求头中 Content-Type没有Form FormData 存放bytes类型 request.method # 请求方式 request.cookies # 浏览器的cookie键值对 request.headers # 请求头 request.path # 路由地址 请求地址 request.url # 请求全部地址 request.host # 主机位
-
session 交由客户端保管机制 保管一串儿加密字符串
from flask import session app.config["SECRET_KEY"] = "@#$%^&*(()*&^%$#*&^&%^&*)" session["key"] = "value"
-
路由配置 route
@app.route( rule, # 路由地址 endpoint, # mapping 名称对应路由地址 url_for 反向推算路由地址 url_for(endpoint) methods, # 允许进入视图函数的 请求方式 redirect_to, # 永久重定向 301 308 strict_slashes, # 是否严格遵循路由匹配规则 defalut, # 默认参数 ) @app.add_url_rule( rule, view_func, # 视图函数 ) # 动态参数路由 @app.route("/get_file/<filename>") def get_file(filename): pass
-
初始化配置
Flask(__name__, template_folder, # 模板存放文件 static_folder, # 静态文件存放目录 static_url_path, # 静态文件访问路径 )
-
config
class DebugConfig(object): DEBUG = True ... app.config.from_object(DebugConfig)
-
蓝图 Blueprint 当成不能被 run 的 Flask 实例
from flask import Blueprint bp = Blueprint("bp",__name__, url_perfix, # url前缀 ) @bp.route("/mybp",endpont="666") @bp.add_url_rule @bp.before_request @bp.after_request @bp.errorhandler(http错误码) url_for("蓝图标识".endpoint) url_for("bp.666") app.register_blueprint(bp)
-
特殊装饰器
@app.before_request # 在请求进入视图函数之前,做出处理 @app.after_request # 在结束视图函数之后,响应客户端之前 @app.errorhandler(http错误码) # 重定向错误信息 def error404(errorMessage): pass
-
CBV
from flask import views class Login(views.MethodView): def get(self,*args,**kwargs): pass app.add_url_rule("/login",endpoint=None,view_func=Login.as_view(name="loginlogin")) # name是当前视图函数名,必须唯一,因为他是endpoint