Django Tornado Flask 对比
- Django 主要特点是大而全,集成了很多组件,例如: Models Admin Form 等等, 不管你用得到用不到,反正它全都有, 属于全能型框架,通常用于大型Web应用由于内置组件足够强大,优点是大而全,缺点也就暴露出来了,这么多的资源一次性全部加载,肯定会造成一部分的资源浪费
- Tornado 主要特点是原生异步非阻塞,在IO密集型应用和多任务处理上占据绝对性的优势,属于专注型框架,通常用于API后端应用,游戏服务后台,其内部实现的异步非阻塞真是稳得一批,优点是异步,缺点是干净,连个Session都不支持
- Flask主要特点小而轻,原生组件几乎为0, 三方提供的组件请参考Django 非常全面,(Flask-admin Flask-SQLAlchemy WTForms)属于短小精悍型框架,通常应用于小型应用和快速构建应用,其强大的三方库,足以支撑一个大型的Web应用,缺点:稳定性相对较差
一、Flask中的Response和request
- Flask的安装 pip install Flask
- return render_template("home.html")#渲染HTML模版返回HTML页面
- return redirect("/")#redirect 跳转到"/" 302临时重定向
- return "hello world!" #HttpResponse
- 特殊返回值:打开并返回文件内容 return send_file("1.png")
- JSON字符串 return json.dumps({"name":"tom"})
- jsonify 标准JSON格式的Response return jsonify({"name":"tom"}) # 相当于多加了这一句Content-Type:application/json
- methods中要放可迭代对象
- URL中的参数数据 request.args
- FormData中的数据 request.form
- to_dict
- 其他:
- request.url #http://127.0.0.1:5000/req?id=123 包含参数
- request.values #坑 args form 中所有数据
- request.path #/req 路由地址
- request.base_url #http://127.0.0.1:5000/req
- request.host #127.0.0.1:5000
- request.host_url #http://127.0.0.1:5000/
- 两个特殊值:
- request.json 要有值 #请求头得是这个Content-Type:application/json
- request.data # 当请求头不被认可的情况,存放原始数据 b""
- Flask中的请求机制(request)request 是基于 mimetype 进行处理的 mimetype的类型 以及 字符串如果不属于上述类型的描述,request就会将无法处理的参数转为Json存入到 data 通过request.data , json.loads 可以拿到里面的参数
from flask import Flask #导入Flask类 from flask import redirect #导入flask中的redirect from flask import render_template #导入flask中的render_template from flask import request #从 flask 包中导入 request 模块 app = Flask(__name__) #实例化Flask对象app @app.route("/") #app中的route装饰器,用来指定试图函数的URL地址 def index():#视图函数 return "hello world!" #HttpResponse #每当访问"/redi"这个地址的时候,视图函数redi会触发redirect("/") 跳转到url地址: "/" 并会触发"/"对应的视图函数index() @app.route("/redi") def redi(): return redirect("/")#redirect 跳转到"/" #注意: 如果要使用 render_template 返回渲染的模板,请在项目的主目录中加入一个目录 templates,否则可能会有一个Jinja2的异常哦 @app.route("/home") def home(): return render_template("home.html")#渲染HTML模版返回HTML页面 @app.route("/req",methods=["POST"]) #methods=["POST"] 代表这个url地址只允许 POST 请求,是个列表也就是意味着可以允许多重请求方式,例如GET之类的 def req(): print(request.method) #POST 可以使用这种方式来验证请求方式 print(request.form) #ImmutableMultiDict([('user','bubu'),('pwd','123')]) Form表单中传递过来的值 使用 request.form拿到 print(request.form["user"]) #bubu print(request.form.get("pwd")) #123 print(list(request.form.keys()))#['user','pwd'] ImmutableMultiDict就是一个字典 return "OK" # @app.route("/req2",methods=["POST","GET"]) @app.route("/req2") def req2(): print(request.url) #http://127.0.0.1:9000/req2 print(request.url_root) #127.0.0.1 - - [05/Aug/2019 00:26:38] "GET /req2 HTTP/1.1" 200 - print(request.script_root) #http://127.0.0.1:9000/ print(request.path) #/req2 print('==========') print(request.args)#request.args 是获取url中的参数 #地址栏http://0.0.0.0:9000/req2?a=1&b=2 打印ImmutableMultiDict([('a', '1'), ('b', '2')]) print('##########') print(type(request.headers)) #<class 'werkzeug.datastructures.EnvironHeaders'> print(request.headers) return "OK" app.run("127.0.0.1",9000,debug=True) #启动Flask Web服务 """ 在Flask 中的HttpResponse 其实就是直接返回字符串 Flask中的Redirect from flask import redirect Flask 中的 render (render_template) Flask中的请求机制(request) 1.request.method 前端用什么方式提交的 2.request.form 是获取form表单中的参数 3.request.args 是获取url中的参数 4.request.values 是参数就都要,坑 <form action="/req2?id=1&age=20" method="post"> <input type="text" name="user"> <input type="text" name="pwd"> <input type="submit" value="提交"> </form> form表单提交的同时使用url参数提交 print(request.values) # CombinedMultiDict([ImmutableMultiDict([('id', '1'), ('age', '20')]), ImmutableMultiDict([('user', 'bubu'), ('pwd', '123')])]) print(request.values.get("id")) # 1 print(request.values["user"]) # bubu # to_dict() 方法可以直接将参数全部转为字典形式 print(request.values.to_dict()) # {'user': 'bubu', 'pwd': '123', 'id': '1', 'age': '20'} ################ 坑:如果url和form中的Key重名的话,form中的同名的key中value会被url中的value覆盖 http://127.0.0.1:5000/req?id=1&user=20 print(request.values.to_dict()) # {'user': 20 'pwd': '123', 'id': '1'} ############### 5.request.cookies 存在浏览器端的字符串也会一起带过来前提是你要开启浏览器的 cookies request.cookies 是将cookies中信息读取出来 6.request.headres 用来获取本次请求的请求头 7.request.data 如果处理不了的就变成字符串存在data里 8.request.files 里面存的是你上传的文件,但是 Flask 在这个文件的操作中加了一定的封装,让操作变得极为简单 <form action="/req2" method="post" enctype="multipart/form-data"> <input type="file" name="file"> <input type="submit" value="提交"> </form> #后端这样写: print(request.files) # ImmutableMultiDict([('file', <FileStorage: 'aa.txt' ('text/plain')>)]) print(request.files["file"]) # <FileStorage: 'aa.txt' ('text/plain')> my_file = request.files["file"] my_file.save("aa.txt") # 保存文件,里面可以写完整路径+文件名 这样我们就成功的保存了一个名叫 "aa.txt" 的文件了 9. request.获取各种路径 没必要记,但是要知道它存在 # 获取当前的url路径 print(request.path)# /req2 # 当前url路径的上一级路径 print(request.script_root) # # 当前url的全部路径 print(request.url) # http://127.0.0.1:9000/req2 # 当前url的路径的上一级全部路径 print(request.url_root ) # http://127.0.0.1:9000/ 10. request.json 前提你得告诉是json 如果在请求中写入了 "application/json" 使用 request.json 则返回json解析数据, 否则返回 None """
二、Flask中的模版语言Jinja2及render_template
- {{}} 取值,执行函数
- {%%} 逻辑代码 if for
- Markup("标签字符串")
- 利用 **{}字典的方式传递参数
- @app.template_global() 后端不用传,前端就可以直接使用的函数
- @app.template_filter() 偏函数,默认就是全局的
- Jinja2模板复用 block
- Jinja2模板语言的模块引用 include
- Jinja2模板语言中的宏定义
from flask import Flask from flask import render_template from flask import Markup # 导入 flask 中的 Markup 模块 app = Flask(__name__) #定义一个函数,把它传递给前端 def a_b_sum(a,b): return a+b #还可以定义全局函数,无需后端传递给前端,Jinja2直接就可以执行的函数 @app.template_global() # 定义全局模板函数 def a_b_sum(a, b): return a + b @app.template_filter() # 定义全局模板函数 def a_b_c_sum(a, b, c): return a + b + c @app.route("/student") def index(): STUDENT = {'name': 'bubu', 'age': 18, 'gender': '女'}, STUDENT_LIST = [ {'name': 'jack', 'age': 18, 'gender': '男'}, {'name': 'tom', 'age': 13, 'gender': '男'}, {'name': 'rose', 'age': 14, 'gender': '女'} ] STUDENT_DICT = { 1: {'name': 'jack', 'age': 18, 'gender': '男'}, 2: {'name': 'tom', 'age': 13, 'gender': '男'}, 3: {'name': 'rose', 'age': 14, 'gender': '女'} } # return render_template("student.html", student=STUDENT,student_list = STUDENT_LIST,student_dict= STUDENT_DICT) #利用 **{}字典的方式传递参数,前端不变 # return render_template("student.html", **{"student":STUDENT ,"student_list" : STUDENT_LIST,"student_dict": STUDENT_DICT}) tag = "<input type='text' name='user' value='tom'>" markup_tag = Markup(tag) #方式2 return render_template("student.html", tag=tag,markup_tag=markup_tag, a_b=a_b_sum) app.run("127.0.0.1",9000,debug=True) #启动Flask Web服务 """ Jinja2模板语言中的 for {% for foo in g %} {% endfor %} Jinja2模板语言中的 if {% if g %} {% elif g %} {% else %} {% endif %} """
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div>{{ student }}</div> <table border="1px"> <tr> <td>{{ student.name }}</td> <td>{{ student["age"] }}</td> <td>{{ student.get("gender") }}</td> </tr> </table> <table border="1px"> {% for foo in student_list %} <tr> <td>{{ foo }}</td> <!--列表中的每一项是字典--> <td>{{ foo.name }}</td> <td>{{ foo.get("age") }}</td> <td>{{ foo["gender"] }}</td> </tr> {% endfor %} </table> <table> {% for foo in student_dict %} <tr> <td>{{ foo }}</td> <!--相当于取出了字典中的key--> <td>{{ student.get(foo).name }}</td> <td>{{ student[foo].get("age") }}</td> <td>{{ student[foo]["gender"] }}</td> </tr> {% endfor %} </table> {{ tag | safe}} <!--方式1--> {{ markup_tag }} <!--方式2--> {{ a_b(99,1) }} <!--函数--> {{ a_b_sum(99,1) }} <!--template_global--> {{ 1 | a_b_c_sum(197,2) }} <!--template_filter 尤其是@app.template_filter() 它的调用方式比较特别,这是两个Flask中的特殊装饰器 --> <!--宏定义一般情况下很少应用到,但是要知道有这么个概念--> {% macro type_text(name,type) %} <input type="{{ type }}" name="{{ name }}" value="{{ name }}"> {% endmacro %} <h2>在下方是使用宏来生成input标签</h2> {{ type_text("one","text") }} {{ type_text("two","text") }} </body> </html> <!--模版复用 index.html中代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>公共的</h1> <h2>下面的内容是不一样的</h2> {% block content %} {% endblock %} <h2>上面的内容是不一样的,但是下面的内容是一样的</h2> <h1>公共的</h1> </body> </html> ###使用时,只有 block 中的内容发生了变化,其他的位置完全一样 {% extends "index.html" %} {% block content %} <form> <input type="text" name="user"> <input type="text" name="pwd"> </form> {% endblock %} --> <!--Jinja2模板语言的模块引用 include login.html 文件中的内容: <form> <input type="text" name="user"> <input type="text" name="pwd"> </form> ###使用时 home.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h2>下面的内容是不一样的</h2> {% include "login.html" %} <h2>上面的内容是不一样的,但是下面的内容是一样的</h2> </body> </html> -->
小例子
from flask import Flask from flask import request from flask import render_template from flask import redirect USER = {'username': 'oldboy', 'password': "oldboy123"} STUDENT_DICT = { 1: {'name': 'Old', 'age': 38, 'gender': '中'}, 2: {'name': 'Boy', 'age': 73, 'gender': '男'}, 3: {'name': 'EDU', 'age': 84, 'gender': '女'}, } app = Flask(__name__) @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": if request.form["username"] == USER["username"] and request.form["password"] == USER["password"]: return redirect("/student_list") return render_template("login.html", msg="用户名密码错误") return render_template("login.html", msg=None) # 如果前端Jinja2模板中使用了msg,这里就算是传递None也要出现msg @app.route("/student_list") def student(): return render_template("student_list.html", student=STUDENT_DICT) @app.route("/info") def student_info(): stu_id = int(request.args["id"]) stu_info = STUDENT_DICT[stu_id] return render_template("student.html", student=stu_info, stu_id=stu_id) app.run("0.0.0.0", 5000, debug=True)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Welcome to Old Boy EDU</title> </head> <body> <form method="post"> 用户名:<input type="text" name="username"> 密码:<input type="text" name="password"> <input type="submit" value="登录"> {{ msg }} </form> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Old Boy EDU</title> </head> <body> Welcome to Old Boy EDU <table border="2xp"> <thead> <tr> <td>id</td> <td>name</td> <td>option</td> </tr> </thead> <tbody> {% for foo in student %} <tr> <td>{{ foo }}</td> <td>{{ student[foo].name }}</td> <td><a href="/info?id={{ foo }}">详细</a></td> </tr> {% endfor %} </tbody> </table> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Old Boy EDU</title> </head> <body> Welcome to Old Boy EDU <table border="1px"> <thead> <tr> <td>id</td> <td>name</td> <td>age</td> <td>gender</td> </tr> </thead> <tbody> <tr> <td>{{ stu_id }}</td> <td>{{ student.name }}</td> <td>{{ student["age"] }}</td> <td>{{ student.get("gender") }}</td> </tr> </tbody> </table> <div><a href="/student_list">返回</a></div> </body> </html>
三、Flask 中内置的 Session
- Flask中的Session非常的奇怪,他会将你的SessionID存放在客户端的Cookie中,使用起来也非常的奇怪
- Flask 中 session 是需要 secret_key 的
- cookies 中 session 存储的是通过 secret_key 加密后的 key , 通过这个 key 从flask程序的内存中找到用户对应的session信息
from flask import Flask #导入Flask类 from flask import request from flask import redirect from flask import render_template from flask import session app = Flask(__name__) app.secret_key = "abcdefg" #secret_key 实际上是用来加密字符串的,如果在实例化的app中没有 secret_key 那么开启session一定会抛异常的 USER = {'username': 'oldboy', 'password': "oldboy123"} STUDENT_DICT = { 1: {'name': 'Old', 'age': 38, 'gender': '中'}, 2: {'name': 'Boy', 'age': 73, 'gender': '男'}, 3: {'name': 'EDU', 'age': 84, 'gender': '女'}, } @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": if request.form["username"] == USER["username"] and request.form["password"] == USER["password"]: session["user"] = USER["username"] #这样用就代表这个请求带上来的session中保存了一个user=name return redirect("/student_list") return render_template("login.html", msg="用户名密码错误") return render_template("login.html", msg=None) # 如果前端Jinja2模板中使用了msg,这里就算是传递None也要出现msg #用 session 进行验证 @app.route("/student_list") def student(): if session.get("user"): return render_template("student_list.html", student=STUDENT_DICT) return redirect("/login") app.run("127.0.0.1",9000,debug=True) """ cookies 中 session 存储的是通过 secret_key 加密后的 key , 通过这个 key 从flask程序的内存中找到用户对应的session信息 """
四、Flask 中路由系统
- 本质就是app.add_url_rule(rule=路由地址, endpoint=反向地址, view_func=视图函数)
- @app.route("/",methods=["GET","POST"])
- @app.route() 装饰器中的参数
- methods : 当前 url 地址,允许访问的请求方式
- endpoint : 反向url地址,默认为视图函数名,解决视图函数重名
- url_for: 反向生成URL地址
- defaults : 视图函数的参数默认值{"nid":1}
- strict_slashes : url地址结尾符"/"的控制 False : 无论结尾 "/" 是否存在均可以访问 , True : 结尾必须不能是 "/"
- redirect_to : url地址重定向301永久重定向,直接不走视图函数
- subdomain : 子域名前缀 subdomian="DragonFire" 这样写可以得到 DragonFire.oldboyedu.com 前提是app.config["SERVER_NAME"] = "oldboyedu.com"
- 动态参数路由 /info/<int:nid> 这样就限制主了参数类,也可以不限制 /info/<nid>
- 路由正则
from flask import Flask from flask import request from flask import url_for app = Flask(__name__) @app.route("/info", methods=["GET", "POST"]) def student_info(): stu_id = int(request.args["id"]) return f"Hello Old boy {stu_id}" # Python3.6的新特性 f"{变量名}" #endpoint : 反向url地址,默认为视图函数名 (url_for) @app.route("/info", methods=["GET", "POST"], endpoint="r_info") def student_info(): print(url_for("r_info")) # /info stu_id = int(request.args["id"]) return f"Hello Old boy {stu_id}" # Python3.6的新特性 f"{变量名}" #defaults : 视图函数的参数默认值{"nid":1} @app.route("/info", methods=["GET", "POST"], endpoint="r_info", defaults={"nid": 100}) def student_info(nid): print(url_for("r_info")) # /info # stu_id = int(request.args["id"]) print(nid) # 100 return f"Hello Old boy {nid}" # Python3.6的新特性 f"{变量名}" #strict_slashes : url地址结尾符"/"的控制 False : 无论结尾 "/" 是否存在均可以访问 , True : 结尾必须不能是 "/" # 访问地址 : /info @app.route("/info", strict_slashes=True) def student_info(): return "Hello Old boy info" # 访问地址 : /infos or /infos/ @app.route("/infos", strict_slashes=False) def student_infos(): return "Hello Old boy infos" #redirect_to : url地址重定向 # 访问地址 : /info 浏览器跳转至 /infos @app.route("/info", strict_slashes=True, redirect_to="/infos") def student_info(): return "Hello Old boy info" @app.route("/infos", strict_slashes=False) def student_infos(): return "Hello Old boy infos" #subdomain : 子域名前缀 subdomian="news" 这样写可以得到 news.oldboyedu.com 前提是app.config["SERVER_NAME"] = "oldboyedu.com" app.config["SERVER_NAME"] = "oldboy.com" @app.route("/info",subdomain="news") def student_info(): return "Hello Old boy info" # 访问地址为: news.oldboy.com/info ############动态参数路由 # 访问地址 : 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 return f"Hello Old boy {nid}" # Python3.6的新特性 f"{变量名}" ######### 路由正则:一般不用 app.run("127.0.0.1",9000,debug=True) """ <int:nid> 就是在url后定义一个参数接收 但是这种动态参数路由,在url_for的时候,一定要将动态参数名+参数值添加进去,否则会抛出参数错误的异常 """
#!/usr/bin/env python # -*- coding: utf-8 -*- from flask import Flask,send_file app = Flask(__name__,template_folder = 'templates') #实例化配置 app.config['SECRET_KEY']='abcdefg' #对象的配置 app.config['DEBUG']=True #对象的配置 app.config['TESTING']=True #测试环境 """ #settings.py class FlaskSetting: DEBUG = True SECRET_KEY = "abcdefg" class FlaskTesting: TESTING=True 使用: import settings app.config.from_object(settings.FlaskSetting) #这叫做类导入配置 """ #动态路由参数 @app.route("/get/<filename>") def get_file(filename): import os path = os.path.join('image',filename) return send_file(path) @app.errorhandler(404) #重定义错误提示 def error404(arg): print(arg)#错误提示 return "404" app.run("0.0.0.0", 5000) """ 补充: <table> {% for k,v in student_dict.items() %} <tr> <td>{{ k }}</td> <!--相当于取出了字典中的key--> <td>{{ v.name }}</td> <td>{{ v.get("age") }}</td> <td> {% if v["gender"] == '中'%} 男 {% else %} {{v['gender']}} {%endif%}</td> </tr> {% endfor %} </table> #实例化蓝图的时候参数比之前的app多了一个url_prefix的参数,可以配置此参数 from flask import Blueprint # 导入 Flask 中的蓝图 Blueprint 模块 sv = Blueprint("sv", __name__,url_prefix="/news") # 实例化一个蓝图(Blueprint)对象 """
五、Flask实例化的配置和对象的配置
- Flask的配置就是在 app.config 中添加一个键值对,但是你存进去的键必须是config中应该存在的,如果不再存在的话,它会默认无用,就这么放着
- static_folder = 'static', # 静态文件目录的路径 默认当前项目中的static目录
- static_url_path = None, # 静态文件目录的url路径 默认不写是与static_folder同名,远程静态文件时复用
- template_folder = 'templates' # template模板目录, 默认当前项目中的 templates 目录
{ 'DEBUG': False, # 是否开启Debug模式 'TESTING': False, # 是否开启测试模式 'PROPAGATE_EXCEPTIONS': None, # 异常传播(是否在控制台打印LOG) 当Debug或者testing开启后,自动为True 'PRESERVE_CONTEXT_ON_EXCEPTION': None, # 一两句话说不清楚,一般不用它 'SECRET_KEY': None, # 之前遇到过,在启用Session的时候,一定要有它 'PERMANENT_SESSION_LIFETIME': 31, # days , Session的生命周期(天)默认31天 'USE_X_SENDFILE': False, # 是否弃用 x_sendfile 'LOGGER_NAME': None, # 日志记录器的名称 'LOGGER_HANDLER_POLICY': 'always', 'SERVER_NAME': None, # 服务访问域名 'APPLICATION_ROOT': None, # 项目的完整路径 'SESSION_COOKIE_NAME': 'session', # 在cookies中存放session加密字符串的名字 'SESSION_COOKIE_DOMAIN': None, # 在哪个域名下会产生session记录在cookies中 'SESSION_COOKIE_PATH': None, # cookies的路径 'SESSION_COOKIE_HTTPONLY': True, # 控制 cookie 是否应被设置 httponly 的标志, 'SESSION_COOKIE_SECURE': False, # 控制 cookie 是否应被设置安全标志 'SESSION_REFRESH_EACH_REQUEST': True, # 这个标志控制永久会话如何刷新 'MAX_CONTENT_LENGTH': None, # 如果设置为字节数, Flask 会拒绝内容长度大于此值的请求进入,并返回一个 413 状态码 'SEND_FILE_MAX_AGE_DEFAULT': 12, # hours 默认缓存控制的最大期限 'TRAP_BAD_REQUEST_ERRORS': False, # 如果这个值被设置为 True ,Flask不会执行 HTTP 异常的错误处理,而是像对待其它异常一样, # 通过异常栈让它冒泡地抛出。这对于需要找出 HTTP 异常源头的可怕调试情形是有用的。 'TRAP_HTTP_EXCEPTIONS': False, # Werkzeug 处理请求中的特定数据的内部数据结构会抛出同样也是“错误的请求”异常的特殊的 key errors 。 # 同样地,为了保持一致,许多操作可以显式地抛出 BadRequest 异常。 # 因为在调试中,你希望准确地找出异常的原因,这个设置用于在这些情形下调试。 # 如果这个值被设置为 True ,你只会得到常规的回溯。 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', # 生成URL的时候如果没有可用的 URL 模式话将使用这个值 'JSON_AS_ASCII': True, # 默认情况下 Flask 使用 ascii 编码来序列化对象。如果这个值被设置为 False , # Flask不会将其编码为 ASCII,并且按原样输出,返回它的 unicode 字符串。 # 比如 jsonfiy 会自动地采用 utf-8 来编码它然后才进行传输。 'JSON_SORT_KEYS': True, #默认情况下 Flask 按照 JSON 对象的键的顺序来序来序列化它。 # 这样做是为了确保键的顺序不会受到字典的哈希种子的影响,从而返回的值每次都是一致的,不会造成无用的额外 HTTP 缓存。 # 你可以通过修改这个配置的值来覆盖默认的操作。但这是不被推荐的做法因为这个默认的行为可能会给你在性能的代价上带来改善。 'JSONIFY_PRETTYPRINT_REGULAR': True, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, }
from flask import Flask app = Flask(__name__) #实例化Flask对象app print(type(app)) #<class 'flask.app.Flask'> app.config["DEBUG"] = True #代码只要发生改动,自动重启Flask程序(app.run) """" Flask的配置就是在 app.config 中添加一个键值对,但是你存进去的键必须是config中应该存在的,如果不再存在的话,它会默认无用,就这么放着 config中有用的key都可以被改写,当然他们也都是有默认值存在的,如果没有特殊情况,不要改写它的默认值 修改配置的方式大约是两种 1.直接对app.config进行修改 app.config["DEBUG"] = True 2.使用类的方式导入 首先要有一个settings.py的文件 #settings.py class FlaskSetting: DEBUG = True SECRET_KEY = "abcdefg" 然后在Flask的启动文件中就可以这么写: from flask import Flask app = Flask(__name__) # type:Flask app.config.from_object("settings.FlaskSetting") 这叫做类导入配置 这是针对一个已经实例化的app进行的配置 """ # 在Flask实例化的时候,传递的参数可以理解为对Flask实例进行的初始配置 static_folder = 'static', # 静态文件目录的路径 默认当前项目中的static目录 static_host = None, # 远程静态文件所用的Host地址,默认为空 static_url_path = None, # 静态文件目录的url路径 默认不写是与static_folder同名,远程静态文件时复用 # host_matching是否开启host主机位匹配,是要与static_host一起使用,如果配置了static_host, 则必须赋值为True # 这里要说明一下,@app.route("/",host="localhost:5000") 就必须要这样写 # host="localhost:5000" 如果主机头不是 localhost:5000 则无法通过当前的路由 host_matching = False, # 如果不是特别需要的话,慎用,否则所有的route 都需要host=""的参数 subdomain_matching = False, # 理论上来说是用来限制SERVER_NAME子域名的,但是目前还没有感觉出来区别在哪里 template_folder = 'templates' # template模板目录, 默认当前项目中的 templates 目录 instance_path = None, # 指向另一个Flask实例的路径 instance_relative_config = False # 是否加载另一个实例的配置 root_path = None # 主模块所在的目录的绝对路径,默认项目目录 #常用的参数有: static_folder = 'static', # 静态文件目录的路径 默认当前项目中的static目录 static_url_path = None, # 静态文件目录的url路径 默认不写是与static_folder同名,远程静态文件时复用 template_folder = 'templates' # template模板目录, 默认当前项目中的 templates 目录
六、Flask 中的蓝图(BluePrint)
- 作用就是将 功能 与 主服务 分开
- 相当于Django中的app01,app02
"""" 比如说,你有一个客户管理系统,最开始的时候,只有一个查看客户列表的功能,后来你又加入了一个添加客户的功能(add_user)模块, 然后又加入了一个删除客户的功能(del_user)模块, 然后又加入了一个修改客户的功能(up_user)模块,在这个系统中,就可以将查看客户,修改客户,添加客户,删除客户的四个功能做成蓝图加入到客户管理系统中 蓝图:可以理解成一个没有run方法的Flask对象 创建一个项目然后将目录结构做成: student_view s_view.py manager.py """ #####s_view.py 文件中的内容 from flask import Blueprint # 导入 Flask 中的蓝图 Blueprint 模块 sv = Blueprint("sv", __name__) # 实例化一个蓝图(Blueprint)对象 @sv.route("/svlist") # 这里添加路由和视图函数的时候与在Flask对象中添加是一样的 def view_list(): return "svlist_view_list" ######manager.py 文件中的内容 from flask import Flask # 导入此前写好的蓝图模块 from student_view import s_view app = Flask(__name__) # type:Flask # 在Flask对象中注册蓝图模块中的蓝图对象 s_view 中的 sv app.register_blueprint(s_view.sv) app.run("127.0.0.1",9000) # 现在Flask对象中并没有写任何的路由和视图函数
"""" 比如说,你有一个客户管理系统,最开始的时候,只有一个查看客户列表的功能,后来你又加入了一个添加客户的功能(add_user)模块, 然后又加入了一个删除客户的功能(del_user)模块, 然后又加入了一个修改客户的功能(up_user)模块,在这个系统中,就可以将查看客户,修改客户,添加客户,删除客户的四个功能做成蓝图加入到客户管理系统中 蓝图:可以理解成一个没有run方法的Flask对象 创建一个项目然后将目录结构做成: student_view sv_static logo.png sv_template svlist.html s_view.py manager.py """ #####s_view.py 文件中的内容 from flask import Blueprint # 导入 Flask 中的蓝图 Blueprint 模块 from flask import render_template sv = Blueprint("sv", __name__, template_folder="sv_template", # 每个蓝图都可以为自己独立出一套template模板文件夹,如果不写则共享项目目录中的templates static_folder="sv_static" # 静态文件目录也是可以独立出来的 ) # 实例化一个蓝图(Blueprint)对象 @sv.route("/svlist") def view_list(): return render_template("svlist.html") #只要Blueprint被 Flask 注册了,就一定会生效 #坑!!! 蓝图内部的视图函数及route不要出现重复 ######manager.py 文件中的内容 from flask import Flask # 导入此前写好的蓝图模块 from student_view import s_view app = Flask(__name__) # type:Flask # 在Flask对象中注册蓝图模块中的蓝图对象 s_view 中的 sv app.register_blueprint(s_view.sv) app.run("127.0.0.1",9000,debug=True) #启动Flask Web服务 # 现在Flask对象中并没有写任何的路由和视图函数
"""" 使用蓝图进行web应用搭建: 创建一个项目然后将目录结构做成: student __init__.py manager.py student_data.py """ #####student/__init__.py 文件中的内容 from flask import Flask def create_app(): app = Flask(__name__) return app ######manager.py 文件中的内容 通过这种方式启动 Flask 程序 from student import create_app flask_app = create_app() flask_app.run("127.0.0.1",9000) ######student_data.py STUDENT = [ {'id': 1, 'name': 'Old', 'age': 38, 'gender': '中'}, {'id': 2, 'name': 'Boy', 'age': 73, 'gender': '男'}, {'id': 3, 'name': 'EDU', 'age': 84, 'gender': '女'} ]
"""" 创建一个项目然后将目录结构做成: student __init__.py student_add html s_add.html stu_add.py student_select html s_list.html stu_select.py student_update html s_update.html stu_update.py manager.py student_data.py """ #####student/__init__.py 文件中的内容 from flask import Flask from student_select import stu_select from student_add import stu_add from student_update import stu_update def create_app(): app = Flask(__name__) # type:Flask app.register_blueprint(stu_select.ss_blueprint) app.register_blueprint(stu_add.s_add) app.register_blueprint(stu_update.s_update) return app ######manager.py 文件中的内容 通过这种方式启动 Flask 程序 from student import create_app flask_app = create_app() flask_app.run("127.0.0.1",9000,debug=True) ######student_data.py STUDENT = [ {'id': 1, 'name': 'Old', 'age': 38, 'gender': '中'}, {'id': 2, 'name': 'Boy', 'age': 73, 'gender': '男'}, {'id': 3, 'name': 'EDU', 'age': 84, 'gender': '女'} ] ######student_add/html/s_add.html """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加学生</title> </head> <body> <form method="post"> ID:<input type="text" name="id"> <br> 姓名:<input type="text" name="name"><br> 年龄:<input type="text" name="age"><br> 性别:<input type="text" name="gender"><br> <input type="submit" value="添加学生"> </form> </body> </html> """ ######student_add/stu_add.py from flask import Blueprint from flask import redirect from flask import request from flask import render_template from student_data import STUDENT s_add = Blueprint("s_add", __name__, template_folder="html", static_folder="static") # type:Blueprint @s_add.route("/s_add",methods=["GET","POST"]) def s_add_view(): if request.method == "POST": stu_dic = { "id": request.form["id"], "name": request.form["name"], "age": request.form["age"], "gender": request.form["gender"] } STUDENT.append(stu_dic) return redirect("/s_list") return render_template("s_add.html") ######student_select/html/s_list.html """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>学生列表</title> </head> <body> <table border="3xp"> <thead> <tr> <td>ID</td> <td>name</td> <td>age</td> <td>gender</td> <td>options</td> </tr> </thead> <tbody> {% for foo in student %} <tr> <td>{{ foo.id }}</td> <td>{{ foo["name"] }}</td> <td>{{ foo.get("age") }}</td> <td>{{ foo.gender }}</td> <td> <a href="/s_update/{{ foo.id }}">修改</a> | <a href="/s_del?id={{ foo.id }}">删除</a> </td> </tr> {% endfor %} </tbody> </table> <a href="/s_add"> 添加学生 </a> </body> </html> """ ######student_select/stu_select.py from flask import Blueprint from flask import render_template from student_data import STUDENT ss_blueprint = Blueprint("ss_b", __name__, template_folder="html", static_folder="static") @ss_blueprint.route("/s_list") def s_list(): return render_template("s_list.html", student=STUDENT) ######student_update/html/s_update.html """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>修改信息</title> </head> <body> <form method="post"> <input type="text" name="id" hidden value="{{ student.id }}"><br> 姓名:<input type="text" name="name" value="{{ student.name }}"><br> 年龄:<input type="text" name="age" value="{{ student.age }}"><br> 性别:<input type="text" name="gender" value="{{ student.gender }}"><br> <input type="submit" value="修改信息"> </form> </body> </html> """ ######student_update/stu_update.py from flask import Blueprint from flask import render_template from flask import redirect from flask import request from student_data import STUDENT s_update = Blueprint("s_update", __name__, template_folder="html", static_folder="static") @s_update.route("/s_update/<int:nid>",methods=["GET","POST"]) def s_update_view(nid): if request.method == "POST": stu_id = int(request.form["id"]) stu_dic = { "id": stu_id, "name": request.form["name"], "age": request.form["age"], "gender": request.form["gender"] } for index,stu in enumerate(STUDENT): if stu["id"] == stu_id: STUDENT[index] = stu_dic return redirect("/s_list") for stu in STUDENT: if stu["id"] == nid : return render_template("s_update.html", student=stu) return render_template("s_update.html", student="")
"""" app static templates views auto.py motor.py __init__.py manager __init__.py就是构建app的一个函数,并且将views中的似乎是蓝图的东西注册进去了 接下来看static目录,这个目录从字面意思就可以理解了,就是我们的static静态文件存放目录了 然后就是templates目录,模板存放目录 views目录,这里存放的就是视图函数文件,也就是Blueprint,每一个文件就是一个Blueprint """ #####app/__init__.py 文件中的内容 from flask import Flask from .views.auto import auto_bp from .views.motor import motor_bp def create_app(): my_app = Flask(__name__) # type:Flask my_app.register_blueprint(auto_bp) my_app.register_blueprint(motor_bp) return my_app ######manager.py 文件中的内容 通过这种方式启动 Flask 程序 from app import create_app my_app = create_app() if __name__ == '__main__': my_app.run() #http://127.0.0.1:5000/ ######views/auto.py from flask import Blueprint auto_bp = Blueprint("auto",__name__) @auto_bp.route("/auto") def auto_func(): return "my_app.auto" ######views/motor.py from flask import Blueprint motor_bp = Blueprint("motor",__name__) @motor_bp.route("/motor") def motor_func(): return "my_app.motor"
七、特殊装饰器
- @app.before_request 在请求(request)进入视图函数之前执行
- @app.after_request 在响应(response)返回客户端之前执行 , 结束视图函数之后
- 正常情况 b1- b2 -b3 views -af3 -af2 - af1 返回顺序是定义代码时的倒序
- 异常情况 b1-af3 -af2 - af1 异常阻塞请求的情况
- @app.errorhandler(404) #重定义错误提示
- @app.before_first_request #第一次访问有 应用场景:升级
""" @app.before_request 在请求(request)进入视图函数之前执行 @app.before_request 也是一个装饰器,他所装饰的函数,都会在请求进入视图函数之前执行 request.path 是来读取当前的url地址如果是 /login 就允许直接通过 return None 你可以理解成通过放行 校验session中是否有user 如果没有的话,证明没有登录,所以毫不留情的 redirect("/login") 跳转登录页面 还有一个要提的 @app.before_first_request 它与 @app.before_request 极为相似或者说是一模一样,只不过它只会被执行一次 """ from flask import Flask from flask import request from flask import redirect from flask import session app = Flask(__name__) # type:Flask app.secret_key = "abcdefg" @app.before_request def is_login(): if request.path == "/login": return None if not session.get("user"): return redirect("/login") @app.route("/login") def login(): return "Login" @app.route("/index") def index(): return "Index" @app.route("/home") def home(): return "Login" app.run("0.0.0.0", 5000) """ @app.after_request 在响应(response)返回客户端之前执行 , 结束视图函数之后,很少应用,但是要了解有这么个东西 """ @app.after_request def foot_log(environ): if request.path != "/login": print("有客人访问了",request.path) return environ
八、CBV
import time from flask import Flask,views,request app=Flask(__name__) # app.secret_key='abcdefg' app.config["SECRET_KEY"] = 'abcdefg' def timer(func): def wrapper(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) stop_time=time.time() print('run time is %s' %(stop_time-start_time)) return res return wrapper class Login(views.MethodView): methods=["GET","POST"] decorators=[timer] #加装饰器 def get(self,*arge,**kwargs): return 'get' def post(self,*arge,**kwargs): my_file = request.files.get("MyFile") my_file.save(my_file.filename)#my_file.filename 原文件名 return 'post' app.add_url_rule('/login',view_func=Login.as_view(name="login")) if __name__ == '__main__': app.run(debug=True)