一、python三大主流框架对比
1、Django:
优点:大而全,在内部集成了很多自己的组件,例如:Model、Admin、Form等,使用起来非常的方便,通常用于大型的web应用。
缺点:因为内部的资源过多,不管会不会用到都是一次想全部加载,会造成一部分资源的浪费。
2、Tornado:
优点:拥有原生的异步非阻塞,在IO密集型应用和多任务处理上占据绝对的优势,属于专注型的框架。
缺点:太精简了,除了异步,内部连session都不支持。
3、Flask:
优点:小而轻,内部的原生组件几乎为0,但是有大量的第三方的组件,非常全面,足以和Django相提并论。通常应用于小型应用和快速构建应用,其强大的三方库,足以支撑一个大型的Web应用
缺点:.........
二、安装以及简单启动
Flask的安装很简单
pip install flask
安装完成之后就可以开始新建一个flask项目了。
首先,新建一个python项目,执行以下代码
from flask import Flask # 导入Flask app = Flask(__name__) # 实例Flask对象app @app.route("/") # app中的route装饰器,url def index(): #视图函数 return "hello python" app.run(debug=True) # 启动Flask Web程序
这样就实现了flask的一个程序。
三、Flask中的三剑客
Django当中最让人熟悉的肯定就是Django当中的三剑客:render、redirect、HttpResponse。在Flask中同样有这三剑客。
1、render_template(如:Django中的render)
在Flask中render的用法与Django中唯一的区别就是名字上。
from flask import render_template # 导入render_template @app.route("/index") def index(): return render_template("index.html") # 返回一个html页面
要是用render_template的话最需要注意的一点就是,我们使用render_template返回渲染的模板肯定需要一个目标这就需要在项目的主目录中加入一个templates目录,注意必须使用这个名字
2、redirect
from flask import redirect # 导入 @app.route("/index") def index(): return redirect(“/”) # 跳转至根目录
与Django中用法相同
3、HttpResponse
@app.route("/") def index(): return "hello python"
这个就简单了,什么都不需要导入,返回HttpResponse就是返回一个字符串
四、Flask中的request对象
每个框架都有相应的处理请求的机制(request),但是每个框架的处理方式还是会有一定的区别的。
想要了解Flask中的处理机制,首先基于HTML和Flask写一个前后端交互的的程序
<form action="" method="post" enctype="multipart/form-data"> 用户名:<input type="text" name="username"> 密码:<input type="password" name="pwd"> <input type="file" name="my_file"> <input type="submit" value="提交"> </form>
在前端页面,先用这样一个form表单接收数据
最主要是后端的接收方式,首先需要导入request模块
from flask import request @apl.route("/login",methods=['POST']) def login(): print(request.method) # 验证请求方式 print(request.form) # 得到一个ImmutableMultiDict([('username', 'sean'), ('pwd', '123')])字典 print(request.form['username']) print(request.form.get("pwd")) print(list(request.form.keys())) # ['username', 'pwd'] print(request.files["my_file"]) return "ok"
其中装饰器@app.route("/login",methods=[ "POST" ]中的methods=[ ' POST' ] 代表只允许POST请求,可以在列表加入多种请求方式,例如GET 。
还有一些比较重要的需要了解的内容:
request.args:可以取到所有的url参数
request.values:取到所有的参数
request.cookie:读取cookie信息
request.headers:读取请求头
request.data:这个是使用request其他方式无法处理的参数都会转为Json存入到data当中,就可以使用request.data,然后json.loads取出数据
request.files:取出文件数据
request.json:这个必须是请求中写入了 "application/json",才可以使用request.json返回json数据
request获取各种路径的方式
# 获取当前的url路径 print(request.path)# /req # 当前url路径的上一级路径 print(request.script_root) # # 当前url的全部路径 print(request.url) # http://127.0.0.1:5000/req # 当前url的路径的上一级全部路径 print(request.url_root ) # http://127.0.0.1:5000/
五、Flask中的模板语言 Jinja2 以及 render_template的使用
在Flask中Jinja2的地位还是比较重要的,毕竟Flask还是基于Jinja2发展的。学习Jinja2首先要在后端定义数据,并将其传到前端页面用模板语言做渲染。
模板语言的流程控制
for : {% for i in list %} ... {% endfor %} if : {% if 条件 %} ... {% elif %} ... {% else %} ... {% endif %}
高阶用法
在Django中如果我们想在后端编写前端代码传到前端进行展示是肯定需要为那段代码加上 safe 参数
在Flask中也有这种方式,不过这个是从前端进行操作的,作为一个后端程序员,为了给前端妹子一个好印象,肯定要做完所有能做的啊,所以在Flask中还有在后端实现的方法
from flask import Flask from flask import render_template from flask import Markup # 导入 flask 中的 Markup 模块 app = Flask(__name__) @app.route("/") def index(): tag = "<input type='text' name='user' value='DragonFire'>" markup_tag = Markup(tag) # Markup帮助咱们在HTML的标签上做了一层封装,让Jinja2模板语言知道这是一个安全的HTML标签 print(markup_tag, type(markup_tag)) # <input type='text' name='user' value='DragonFire'> <class 'markupsafe.Markup'> return render_template("index.html", tag=markup_tag) app.run("0.0.0.0", 5000, debug=True)
这种方法与safe在界面上展示的效果相同。
在模板中执行python函数
后端代码:
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 @app.route("/") def index(): return render_template("index.html", tag=a_b_sum) app.run("0.0.0.0", 5000, debug=True)
前端代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ tag }} <br> {{ tag(99,1) }} </body> </html>
定义全局函数,无需后端传递给前端,Jinja2直接就可以执行的函数
后端:
from flask import Flask from flask import render_template from flask import Markup # 导入 flask 中的 Markup 模块 app = Flask(__name__) @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("/") def index(): return render_template("index.html", tag="") app.run("0.0.0.0", 5000, debug=True)
前端:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ a_b_sum(99,1) }} <br> {{ 1 | a_b_c_sum(197,2) }} </body> </html>
还有一些母版、组件之类的东西都是与Django当中的用法是一样的
最后还有一个Flask中的宏定义,相当于就是在HTML文件中定义类似后端代码中的函数的类型
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>Welcome OldboyEDU</h1> {% 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>
六、Flask中内置的session
在Flask中,session会存放到客户端的Cookie当中,使用起来也与其他的框架有些区别
1、配置Flask中session需要的secret_key
from flask import session app = Flask(__name__) app.secret_key = "sean"
secret_key主要是用中没有secret来加密字符串的,如果在实例化的app中没有secret_key的话,当我们使用到session的时候会抛出异常。
2、session使用方式
@apl.route("/login",methods=['GET','POST']) def login(): if request.method == "GET": return render_template("login.html") else: print(request.method) username = request.form['username'] pwd = request.form['pwd'] if username == "sean" and pwd == "123": session["user"] = username return redirect("/index") return render_template("login.html", msg="用户名或密码错误!!")
3、浏览器cookie中的session
cookies 中 session 存储的是通过 secret_key 加密后的 key , 通过这个 key 从flask程序的内存中找到用户对应的session信息
4、使用session进行验证
@app.route("/index") def index(): if session.get("user"): return render_template("index.html", student=STUDENT_DICT) return redirect("/login")
七、路由系统 (url_for)
Flask中的路由系统我们从一开始都在用,@app.route("/",methods=["GET","POST"]),刚开始也都是只知道怎么用,但是为什么要这么用也没有深究过..
1、@app.route()装饰其中的参数
methods:当前url路径可以访问的请求方式
@apl.route("/login",methods=['GET','POST'])
endpoint:反向url地址,默认为视图函数名(url_for)
from flask import 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"{变量名}"
当有多个视图函数需要用到session的时候,最简单的方法就是自定义一个session装饰器,将需要用到session的视图函数都通过装饰器装饰起来。
def wrapper(func): def inner(*args,**kwargs): if session.get("user"): return func(*args,**kwargs) return redirect("/login") return inner @apl.route("/index") @wrapper def index(): return render_template("index.html",stu = STUDENT_LIST) @apl.route("/index2") @wrapper def index2(): input_tag = Markup("<input type='text' name='input_tag'>") return render_template("index.html", stu = STUDENT_LIST, input_tag=input_tag)
但是如果真的使用这种方式的话肯定会抛出异常的,因为装饰最后返回的都是inner函数,每一个url装饰器装饰的都是这一个函数,肯定会有问题。
这就需要用到这个神奇的参数endpoint了,它会通过这个别名反向解析url,最后每个url装饰器装饰的就都是它自己的视图函数了。
def wrapper(func): def inner(*args,**kwargs): if session.get("user"): return func(*args,**kwargs) return redirect("/login") return inner @apl.route("/index/<string:nid>",endpoint="baby1") @wrapper def index(nid): print(url_for("baby1",nid = nid)) return render_template("index.html",stu = STUDENT_LIST) @apl.route("/index2",endpoint="baby2") @wrapper def index2(): input_tag = Markup("<input type='text' name='input_tag'>") return render_template("index.html", stu = STUDENT_LIST, input_tag=input_tag)
strict_slashes :url地址结尾符"/"的控制 False : 无论结尾 "/" 是否存在均可以访问 , True : 结尾必须不能是 "/"
redirect_to : url地址重定向
defaults:路由默认参数
<int:nid>:动态参数,可以指定数据类型
八、实例化参数以及 各种配置
Flask中的配置是灵活多变的,都需要我们自己来配置
from flask import Flask app = Flask(__name__) # type:Flask app.config["DEBUG"] = True
以上代码中的app.config["DEBUG"] = True就是对Flask的配置,正常情况下Flask不会像Django那样自动重启项目,但是加上这个配置就可以让Flask可以在我们修改了项目中代码的情况下自动重启,这就是Flask的调试模式。
Flask的配置就是在app下的config中添加相应的键值对,但是必须是Flask中内置的才可以
{ '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, }
以上配置都可以使用,但是它们也都是有默认值的,没有特殊情况就不要修改他们的默认值
修改配置的方式大约是两种
1.直接对app.config进行修改
app.config["DEBUG"] = True
2.使用类的方式导入
首先要有一个settings.py的文件
class FlaskSetting: DEBUG = True SECRET_KEY = "DragonFire"
然后我们在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
Flask中的蓝图 Blueprint 它主要就是将我们的主服务与功能分开,可以将不同的功能写在不同的模块当中,然后将这些不同的功能全部加到主服务当中。
1、初识蓝图
项目结构
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("0.0.0.0",5000) # 现在Flask对象中并没有写任何的路由和视图函数
在Flask对象中并没有添加路由,到浏览器中依然可以访问我们从蓝图对象中注册的的路由以及视图函数
自定义蓝图在实例化的时候我们还需要传递一些参数,具体是么作用,实践一下....
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")
其实通过这些也可以看出蓝图其实也看成是一个没有run方法的Flask对象,只需要被Flask注册一下就会生效
注意:
各个蓝图之间的视图函数以及route不可以出现重复,否则是会出异常的
十、其他
以下方法都是Flask对象中的装饰器,当我们的视图函数想要有以下需求就不需要自己苦思冥想了,会用下面的装饰器就可实现功能。
很少有应用,做简单了解
1、before_request : 请求之前做处理,比如methods的请求方法
2、after_requst : 视图函数返回后做处理
3、before_frist_request : 第一次请求之前做处理
4、errorheadler : 定义错误信息 def error_page(error_massage)
flash
通过flash("message")进行一次存放,就会在内存中有这个参数,直到有get_flashed_messages() 方法提取,这个参数就会永久消失