Flask是一个基于Python开发而且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,而后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,若是要返回给用户复杂的内容时,须要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。html
“微”(micro) 并不表示你须要把整个 Web 应用塞进单个 Python 文件(虽然确实能够 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你作出太多决策——好比使用何种数据库。而那些 Flask 所选择的——好比使用何种模板引擎——则很容易替换。除此以外的一切都由可由你掌握。如此,Flask 能够与您珠联璧合。前端
默认状况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库能够胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 自己实现的同样。众多的扩展提供了数据库集成、表单验证、上传处理、各类各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用。node
初始Flask框架,将会从下面11个方向进行介绍。python
1. 介绍Flask、Django、Tornado框架
2. Flask快速入门
3. 配置文件
4. 路由系统
5. 模板语言
6. 请求&响应相关
7. session & cookie
8. 闪现
9. 蓝图
10. 请求扩展(django 中间件)
11. 中间件mysql
Django:重武器,大而全。内部包含了很是多组件:ORM、Form、ModelForm、缓存、Session、中间件、信号等...大型项目用Django的比较多
Flask:短小精悍,可扩展。内部没有太多组件,可是第三方组件很是丰富。
路由比较特殊:基于装饰器来实现,可是究其本质仍是经过add_url_rule来实现。web
小项目通常用Flask的比较多,同时由于Flask有很是多的第三方组件,也能够很方便的实现Django具有的功能正则表达式
Tornado:异步非阻塞框架(node.js)redis
1. 安装Flasksql
为了避免与现行项目想冲突,一般状况下会引入一个虚拟环境,将Flask安装在虚拟环境下,虚拟环境用virtualenv(关于virtualenv后面再专门写一篇帖子介绍它)数据库
为了方便快速建立,全部过程都在Pycharm里完成
(1)使用Virtualenv建立虚拟环境
(2)将Flask安装在该虚拟环境里
2.WSGI
对于真实开发中的python web程序来讲,通常会分为两部分:服务器程序和应用程序。
服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各类数据进行整理。
应用程序则负责具体的逻辑处理。
为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不一样的框架有不一样的开发方式,可是不管如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。这样,服务器程序就须要为不一样的框架提供不一样的支持。这样混乱的局面不管对于服务器仍是框架,都是很差的。对服务器来讲,须要支持各类不一样框架,对框架来讲,只有支持它的服务器才能被开发出的应用使用。这时候,标准化就变得尤其重要。能够设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就能够配合使用。一旦标准肯定,双方各自实现。这样,服务器能够支持更多支持标准的框架,框架也可使用更多支持标准的服务器。
WSGI(Web Server Gateway Interface)是一种规范,它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦。
python标准库提供的独立WSGI服务器称为wsgiref。
# werkzeug示例: from werkzeug.wrappers import Request, Response @Request.application def hello(request): return Response('Hello World!') if __name__ == '__main__': from werkzeug.serving import run_simple run_simple('localhost', 4000, hello) # wsgiref示例: from wsgiref.simple_server import make_server def runserver(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ] # 本质的本质: import socket def handle_request(client): buf = client.recv(1024) client.send("HTTP/1.1 200 OK\r\n\r\n") client.send("Hello, Seven") def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('localhost',8000)) sock.listen(5) while True: connection, address = sock.accept() handle_request(connection) connection.close() if __name__ == '__main__': main()
3.Flask
第一个简单的demo
# 1. 导入Flask from flask import Flask # 2. 实例化Flask对象 app = Flask(__name__) app.debug = True # 每次修改完成后,这个语句就会自动重启服务,省去开发者手动重启 ''' 看到这种加()的装饰器,就要知道它里面其实执行了2个步骤: 1. 先执行app.route('/'),把返回值赋给 v, 即v = app.route('/') 2. 在执行v(), 并把hello_world做为参数传给v,即v(hello_world) ''' # 3. 做用:将"/"和函数index的对应关系添加到路由中 @app.route('/') def index(): return 'Hello World!' if __name__ == '__main__': # 4. 监听用户请求 # 若是有用户请求过来,则执行app的__call__方法 # 1. run里不写内容,默认用用的就是本机IP,端口是5000 ;http://127.0.0.1:5000/ app.run() # 2.run里写了IP和端口,就使用传的IP和端口运行服务 app.run( host = 'localhost', # localhost =127.0.0.1,表明本机; 若是此处是0.0.0.0,表明任意ip的请求都是运行的 port = 9999 )
以用户登陆做为一个简单的demo,并一步步填充知识点:
需求一:实现一个简单登陆页面,若是用户名密码校验成功,跳转到百度首页;若是校验失败,返回到登陆页面,并给出错误提示
用户登陆页面
from flask import Flask,render_template,request,redirect app = Flask(__name__)
# 将构造函数的 name 参数传给 Flask 程序,Flask 用这个参数决定程序的根目录,以便稍后可以找到相对于程 序根目录的资源文件位置
客户端(例如 Web 浏览器)把请求发送给 Web 服务器,Web 服务器再把请求发送给 Flask程序实例。
程序实例须要知道对每一个 URL 请求运行哪些代码,因此保存了一个 URL 到 Python 函数的映射关系。
处理 URL 和函数之间关系的程序称为路由
在 Flask 程序中定义路由的最简便方式,是使用程序实例提供的 app.route 修饰器,把修饰的函数注册为路由。 # 1. app.route()中:第一个参数指明了登陆页面的路径,methods参数指明了能够接受的请求方式,不写的时候默认接受GET请求 @app.route("/login", methods=['GET', 'POST']) def login(): ''' 2. 登陆函数里须要返回一个登陆页面,须要用到render方法,在flask里导入render_template模块 ender_template("login.html")指明了模板路径 模板路径不能随便指定,Python里规定了必须写在templates目录下,因此须要先建立该目录 而后再templates目录下建立login.html页面''' ''' 3. 请求相关的数据过来后,所有放在request里,此时须要先导入request 同时,须要判断过来的请求是什么方式,并执行相关的操做请求逻辑 ''' if request.method =="GET": return render_template("login.html") else: # url中的请求数据所有放在query_string # request.query_string # 请求体相关的数据都放在form中,并且必定要get获取前端传的用户名和密码后才能传给Server,这里曾经犯了一个严重错误,没有get,数据没法传递给厚度,致使if逻辑没有处理 user = request.form.get("username") pwd = request.form.get("password") ''' 4.而后就须要去数据库里查找比对是否存在这个用户,此处先略去数据库的操做 ''' if user == "alex" and pwd =="123456": ''' 5.若是正确就会跳转到登陆成功后的某个页面,须要用到redirect方法,没有就去导入''' return redirect("www.baidu.com") '''6. 若是用户名不对,仍是跳回登陆页面,同时提示用户名或者密码错误; 错误提示信息须要在页面展现,那login.html就须要有个变量名接受这个错误信息 ''' return render_template("login.html",error="输入的用户名或密码错误") return render_template("login.html", **{"error":"用户名和密码错误"}) # **context:能够接受字典方式的参数,可是书写要注意: # 1. 能够直接以赋值的方式写,如error="输入的用户名或密码错误" # 2. 也能够以字典的方式写,可是要注意,若是以字典方式,须要在{}前加两个**。 如上 if __name__ =="__main__": app.run()
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登陆页面</title> </head> <body> <h1>用户登陆</h1> <form method = "post"> # 知识点:表单数据,必定要加在<form ></form>体内 <input type="text" name="username"> <input type="text" name="password"> <input type="submit" value="登 录"> # 烦的错误:登陆按钮type="submit", 可是按钮名没有写成value,致使按钮上没有显示登陆两个字 <br> {{error}} # 知识点:接受后端传送的值,须要经过花括号{{变量名}}的方式接受 </form> </body> </html>
需求二: 作一个首页,其内容展现出一个用户列表
首页-用户列表页
from flask import Flask,render_template,request,redirect app = Flask(__name__) app.debug = True # 1. 没有数据库,先用一个字典做为一个用户列表 USERS = { 1:{"name":"老大", "age":19,"gender":"男", "resume":"言语措辞间都能体会到今日头条的谨小慎微,生怕再出现任何问题……."}, 2:{"name":"老二", "age":18,"gender":"女", "resume":"当打开这款APP以后,就会发现这跟已经“死”去的内涵段子简直是如出一辙。更厉害的地方是,它还支持用用户迁移内涵段子上的内容信息。"}, 3:{"name":"老三", "age":17,"gender":"男", "resume":"若是狒狒会说人话,他确定是在说:喂…你怎么只给我吃了一口就跑了呀,我还没吃饱啊…喂喂喂…给我回来呀!哈哈哈"}, } # 2. 定义一个首页-用户列表页 @app.route("/index", methods=['GET']) # 首页通常都是先get数据的,因此methods用get方法 # 将USERS复制给变量user_dict,前端拿到该变量将会用来循环获取数据 def index(): return render_template("index.html", user_dict=USERS) @app.route("/login", methods=['GET', 'POST']) def login(): if request.method =="GET": return render_template("login.html") else: user = request.form.get("username") pwd = request.form.get("password") if user == "alex" and pwd =="123456": return redirect("https://www.baidu.com") return render_template("login.html", **{"error":"用户名和密码错误"}) if __name__ =="__main__": app.run()
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>用户页面</h1> <!--用户列表是一个表格, 用<table> </table> --> <table> <!-- 后端传给前端的是一个用户列表,前端须要接受,并提取出须要的数据展现,这就须要用到循环了 知识点: 1. 在html里循环,须要以{% for循环条件 %}开始,以{% endfor %} 结尾 2. 循环结果展现也要以{{ }} 接受变量 3. v也是一个字典,在Flask里,字典值的获取有三种方式: 1) v.name 以.的方式直接获取 2) v['name'] 以索引的方式获取 3) v.get('name') 以get的方式获取 --> {% for k,v in user_dict.items() %} <tr> <td>{{ k }}</td> <td>{{ v.name }}</td> <td>{{ v['name'] }}</td> <td>{{ v.get('name') }}</td> <td>{{ v.age }}</td> <td>{{ v.get('gender') }}</td> <!-- 详细信息页,下面要用到--> <!-- a标签是超连接的标志 --> <!-- 须要给详细信息detail页传一个id,后端根据该id来决定显示什么数据给前端,ID也是花括号方式接受 --> <!-- 同时,后端须要一个地址来接受前端传的值,定义个路由"/detail/id"地址来接受 --> <td><a href="detail/{{ key }}">查看详细信息</a></td> </tr> {% endfor %} </table> </body> </html>
需求三: 首页内容展现出一个用户列表,点击详细信息能够查看用户的具体详细信息
详细信息页
from flask import Flask,render_template,request,redirect app = Flask(__name__) app.debug = True USERS = { 1:{"name":"老大", "age":19,"gender":"男", "resume":"言语措辞间都能体会到今日头条的谨小慎微,生怕再出现任何问题……."}, 2:{"name":"老二", "age":18,"gender":"女", "resume":"当打开这款APP以后,就会发现这跟已经“死”去的内涵段子简直是如出一辙。更厉害的地方是,它还支持用用户迁移内涵段子上的内容信息。"}, 3:{"name":"老三", "age":17,"gender":"男", "resume":"若是狒狒会说人话,他确定是在说:喂…你怎么只给我吃了一口就跑了呀,我还没吃饱啊…喂喂喂…给我回来呀!哈哈哈"}, } # 定义用户信息详情页,须要根据前端传的id决定具体展现哪一个用户的详细信息,并且该id还须要是动态的 # Flask里动态接受,有个规则,须要用<类型:nid>的方式 # int:表示前端传的要是一个数值类型 # nid: 表示传的值 # 同时,前端传值了,就须要在detail函数里接收这个值 @app.route("/detail/<int:nid>", methods=['GET']) def detail(nid): # 接收前端传的数值类型的值 detail_info = USERS.get(nid) # USERS用户列表里根据nid获取值 return render_template("detail.html",info = detail_info) # info = detial_info 接受上面USERS里获取到的值,并给前端显示 @app.route("/index", methods=['GET']) def index(): return render_template("index.html", user_dict=USERS) @app.route("/login", methods=['GET', 'POST']) def login(): if request.method =="GET": return render_template("login.html") else: user = request.form.get("username") pwd = request.form.get("password") if user == "alex" and pwd =="123456": return redirect("https://www.baidu.com") return render_template("login.html", **{"error":"用户名和密码错误"}) if __name__ =="__main__": app.run()
detail.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>详细信息 {{info.name}}</h1> <div> {{info.text}} </div> </body> </html>
需求四:实现登陆成功后,能够正常访问上面的页面,因此须要引入session。(Flask里有session,可直接引入),若是不用session就要本身写cookie了。
from flask import Flask,render_template,request,redirect,session,url_for app = Flask(__name__) app.debug = True USERS = { 1:{"name":"老大", "age":19,"gender":"男", "resume":"言语措辞间都能体会到今日头条的谨小慎微,生怕再出现任何问题……."}, 2:{"name":"老二", "age":18,"gender":"女", "resume":"当打开这款APP以后,就会发现这跟已经“死”去的内涵段子简直是如出一辙。更厉害的地方是,它还支持用用户迁移内涵段子上的内容信息。"}, 3:{"name":"老三", "age":17,"gender":"男", "resume":"若是狒狒会说人话,他确定是在说:喂…你怎么只给我吃了一口就跑了呀,我还没吃饱啊…喂喂喂…给我回来呀!哈哈哈"}, } @app.route("/detail/<int:nid>", methods=['GET']) def detail(nid): # 校验session,获取值 user = session.get('user_info') # 若是获得的session没值,不是该登陆用户,那就重定向到登陆页面 if not user: ''' 知识点: 一. 利用别名进行反向生成 1. redirect时跳转到某个url,若是这个url特别长,很容易写出错,怎么搞? 能够用别名的方式代替,根据别名能够反向生成 别名从哪里来?你要redirect到哪一个页面,就在那个路由上起别名,用endpoint 2. 要根据别名进行反向生成,还需导入一个 url_for的模块,url_for的做用就是进行反向生成 3. end_for也是能够接收参数的,参数是**values,好比dtail后面会接受id就能够写成 url = url_for('l1',nid = nid)#是这么写吧,暂定? 二. 写一个装饰器,代替每一个函数里session校验,进行用户认证 ''' url = url_for('l1') # 根据别名l1反向生成/login的地址,并复制给url return redirect(url) # 重定向跳转到url就能够 detail_info = USERS.get(nid) # 若是有值,就跳到详情页 return render_template("detail.html",info = detail_info) # info = detial_info 接受上面USERS里获取到的值,并给前端显示 @app.route("/index", methods=['GET']) def index(): # 没有通别名的方式进行反向生成的方式跳转 user = session.get("user_info") if not user: return redirect("/login") return render_template("index.html", user_dict=USERS) # ➕假设登陆页url很长,给起个别名 endpoint = l1 @app.route("/login", methods=['GET', 'POST'], endpoint='l1') def login(): if request.method =="GET": return render_template("login.html") else: user = request.form.get("username") pwd = request.form.get("password") # 用户登陆成功以前,写上session,让其 = 用户名,至关于给session里的user_info键赋值了 # 当用户登陆成功后,session里其实就有值了,访问其余页面时,每个都须要进行判断session # 因此,必定会想到用装饰器的方式解决 if user == "alex" and pwd =="123456": # session赋值的动做必定是咋用户登陆信息校验经过后才获取已登陆的用户名赋值给session下的'user_info的 # 犯的错误:把session['user_info'] = user 写到了if判断的外面,致使报错 session['user_info'] = user return redirect("https://www.baidu.com") return render_template("login.html", **{"error":"用户名和密码错误"}) if __name__ =="__main__": app.run()
装饰器:关于上面的知识点二,完成装饰器,须要注意的三个点
1. 装饰器通常是两层函数嵌套
2. 一个函数能够用多个装饰器,当用多个装饰器的时候,它的执行顺序是怎样的?
3. 若是url的别名重复了,是不容许的。也就是对于反向查找的名称不容许重复。flask里经过endpoint解决的
先看个简单的配置的示例
# 配置文件 from flask import Flask app = Flask(__name__) # 两个简单的配置 app.debug = True app.secret_key = "34ufad342333mfkf" @app.route("/") def index(): return "Hello World" if __name__ == "__main__": app.run()
问题:Falsk里的配置文件有不少的时候,都须要写到Python代码里吗?答案是否认的
1. Flask都有哪些配置文件?
flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为: { 'DEBUG': get_debug_flag(default=False), 是否开启Debug模式 'TESTING': False, 是否开启测试模式 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), 'USE_X_SENDFILE': False, 'LOGGER_NAME': None, 'LOGGER_HANDLER_POLICY': 'always', 'SERVER_NAME': None, 'APPLICATION_ROOT': None, 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), 'TRAP_BAD_REQUEST_ERRORS': False, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': True, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, }
2. Flask都有哪些方式设置配置文件?有三种方式
(1)方法1
# 配置文件 from flask import Flask app = Flask(__name__) # 两个简单的配置文件 app.debug = True app.secret_key = "34ufad342333mfkf" # 方法1 app.config['debug'] = True app.config['secret_key'] = "2343urioeurq" # PS: 因为Config对象本质上是字典,因此还可使用app.config.update(...) @app.route("/") def index(): return "Hello World" if __name__ == "__main__": app.run()
(2) 方法2: 以py文件的方式导入
配置文件须要写在同级目录下
app.config.from_pyfile("python文件名称")
app.config.from_pyfile("python文件名称") 如: settings.py DEBUG = True app.config.from_pyfile("settings.py")
app.config.from_envvar("环境变量名称") 环境变量的值为python文件名称名称,内部调用from_pyfile方法 app.config.from_json("json文件名称") JSON文件名称,必须是json格式,由于内部会执行json.loads app.config.from_mapping({'DEBUG':True}) 字典格式 PS: 从sys.path中已经存在路径开始写 PS: settings配置文件可能会存在于两个路径下面:
1. settings.py文件默认路径要放在程序root_path目录(即根目录下),
2. 若是instance_relative_config = True 时,settings.py 文件就要放在instance_path目录下
(3)方法3:以文件的方式进行配置,而后导入配置文件
1. 先在同级目录下,新建 settings.py文件,配置项须要大写
DEBUG = True # 配置项须要大写
2.在须要使用到配置文件的地方导入
from flask import Flask app = Flask(__name__) # 以from_pyfile方式导入配置文件 app.config.from_pyfile("settings.py") @app.route("/") def index(): return "Hello World!" if __name__ =="__main__": app.run()
(4)方法4:最经常使用的方式,以from_objec方式 ----推荐使用的方式
# 以from_object方式导入配置文件
app.config.from_object("python类或者类的路径")
# 表示FalskLearn目录下面的settings文件下面有个DevelopmentConfig类
app.config.from_object("pro_flask.settings.DevelopmentConfig")
Python类写在settings.py配置文件里
# 生产环境默认配置 class Config(): DEBUG = False TESTING = False DATABASE_URL = "sqlite://:memory:" # 测试或开发环境继承Config类,重写相关配置项 class ProductionConfig(Config): DATABASE_URL = "mysql://user@localhost/foo" class DevelopmentConfig(Config): DEBUG = True class TestingConfig(Config): TESTING = True
导入配置文件示例:
from flask import Flask app = Flask(__name__) # 以from_object方式导入配置文件 # app.config.from_object("python类或者类的路径") # 由于 settings.py文件与当前文件同级,因此settings前没再写它的目录 app.config.from_object("settings.DevelopmentConfig") @app.route("/") def index(): return "Hello World!" if __name__ =="__main__": app.run()
经常使用路由系统有以上五种,全部的路由系统都是基于一下对应关系来处理:
DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
(1)路由系统的本质
from flask import Flask app = Flask(__name__) app.config.from_object("settings.DevelopmentConfig") ''' 看到路由系统,分析路由系统干了什么? 第一步:先执行:decorator = app.route("/", methods= ['GET','POST'], endpoint='n1') 根据源码,第一步执行了下面的函数,返回decorator,这就是一个闭包,闭包给谁用,谁之后执行这个函数就给谁用 def route(self, rule, **options): # 具体值与参数的对应关系: # app对象 # rule = "/" # options = {methods= ['GET','POST'], endpoint='n1'} def decorator(f): endpoint = options.pop('endpoint', None) self.add_url_rule(rule, endpoint, f, **options) return f return decorator 第二步: 第一步返回了decorator,因此至关因而: @decorator,等价于decorator(index),触发上面route函数下面的decorator函数的运行 def decorator(f): endpoint = options.pop('endpoint', None) self.add_url_rule(rule, endpoint, f, **options) # self就是app对象。加到了路由的对应表里去了,那里有url对应的函数 return f 最后总结一下,添加路由关系的本质,其实就是最终执行 self.add_url_rule(rule, endpoint, f, **options),生成路由的对应关系 ''' # 对于endpoint还要注意: # 若是endpoint没有写,根据源码可知,默认的就是函数名,即该路由对应函数,如index(endpoint = index) @app.route("/", methods= ['GET','POST'], endpoint='n1') def index(): return "Hello World!" def login(): return "登陆" # 因此根据源码的原理,也能够按照下面的方式添加路由对应关系,就与Django相似了。可是在Flask里,仍是要按照装饰器的方式去添加路由对应关系 app.add_url_rule('/login', 'n2', login, methods= ['GET','POST']) if __name__ == "__main__": app.run()
(2)路由系统值CBV
上面的路由是一种FBV的写法,在Flask里,还支持CBV的写法
from flask import Flask,views app = Flask(__name__) app.config.from_object("settings.DevelopmentConfig") def auth(func): def inner(*args, **kwargs): result = func(*args, **kwargs) return result return inner # 1. 继承自views.MethodView 采用CBV写法时,为了简单,都是采用继承MethodView的方式写的 class IndexView(views.MethodView): methods = ['GET'] decorators = [auth,] def get(self): return "Index.GET" def post(self): return "Index.POST" # view_func = 类.as_view # name="index" 指的就是endpoint # name = endpoint app.add_url_rule('/index', view_func=IndexView.as_view(name="index")) if __name__ == "__main__": app.run()
或
from flask import Flask,views app = Flask(__name__) app.config.from_object("settings.DevelopmentConfig") def auth(func): def inner(*args, **kwargs): print('before') result = func(*args, **kwargs) print('after') return result return inner # 也能够再往上继承自View class IndexView(views.View): methods = ['GET'] decorators = [auth, ] # 若是继承自View,就须要dispatch_request def dispatch_request(self): print('Index') return 'Index!' app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name=endpoint if __name__ == "__main__": app.run()
(3)@app.route和app.add_url_rule参数:
A. 最经常使用参数
rule, URL规则 view_func, 视图函数名称 defaults=None, 默认值,当URL中无参数,函数须要参数时,使用defaults={'k':'v'}为函数提供参数 endpoint=None, 名称,用于反向生成URL,即: url_for('名称') methods=None, 容许的请求方式,如:["GET","POST"]
from flask import Flask app = Flask(__name__) # 以from_object方式导入配置文件 # app.config.from_object("python类或者类的路径") app.config.from_object("settings.DevelopmentConfig") @app.route("/",methods=['GET','POST'],) # 1. 函数index须要参数,可是url里无参数 def index(nid): return "Hello World!" if __name__ =="__main__": app.run()
from flask import Flask app = Flask(__name__) # 2. 函数index须要参数,可是url里无参数,便可以用defaults={"k":"v"}为函数提供参数 @app.route("/",methods=['GET','POST'],defaults={"nid":888}) # 1. 函数index须要参数,可是url里无参数 def index(nid): print(nid) # 会在工做它打印nid 888 return "Hello World!" if __name__ =="__main__": app.run()
B. strict_slashes = None,表示对URL最后的 / 符号是否有严格要求
from flask import Flask app = Flask(__name__) # strict_slashes=False表示访问http://127.0.0.1:5000/index 或者 http://127.0.0.1:5000/index/ 均可以 @app.route("/index",methods=['GET','POST'],strict_slashes=False) def index(): return "Hello World!" # strict_slashes=True 表示访问http://127.0.0.1:5000/login时,login后面有没有/, # 必须与@app.route("/login")的"/login"一致才能够 @app.route("/login",methods=['GET','POST'],strict_slashes=True) def login(): return "Hello World!" if __name__ =="__main__": app.run()
C.redirect_to = None : 重定向到指定地址
from flask import Flask app = Flask(__name__) app.Debug = True # 不少状况下,公司网站改版,都会有一段时间新老网站都会共存,不少用户都会点收藏的老的网站地址,须要能跳转到新的网站 # 能够用redirect_to进行重定向到新网站 @app.route("/index",methods=['GET','POST'],endpoint="n1", redirect_to="/index2") def index(): return "老网站"
# 或者 def index(): return "老网站" @app.route("/index",methods=['GET','POST'],endpoint="n1", redirect_to="index2") # 新网站 @app.route("/index2",methods=['GET','POST'],endpoint='n2') def index2(): return "新网站" if __name__ =="__main__": app.run()
D.subdomain = None: 子域名访问
一般状况下,公司都会有个官网,好比www.baidu.com, 可是也会存在一些子域名,好比:api.baidu.com; admin.baidu.com
须要在hosts里配置上对应关系
wins: C:\Windows\System32\drivers\etc\hosts
mac: /etc/hosts
from flask import Flask app = Flask(__name__) # 子域名方式,SERVER_NAME 是必需要配置的 # body.com是域名,可是目前这个程序运行ip是127.0.0.1 dns域名解析 # 因此要在hosts里把域名和ip进行配置对应关系 app.config['SERVER_NAME']='body.com:5000' @app.route('/',subdomain='admin') def static_index(): return 'static.body.com' # 子域名若是是写死的,访问地址以下: # admin.body.com:5000
# 示例及运行结果
# subdomain='<domain>',<>表示相似于一个字符串形式的正则表达式,传什么子域名就显示什么子域名 @app.route('/dyaciton',subdomain='<domain>') def domain_indext(domain): return domain+".body.com" # 子域名若是是以变量方式传入,访问地址以下: # buy.body.com:5000/dyaciton # admin.body.com:500/dyaction
# 示例及运行结果
if __name__=="__main__": app.run()
(4) Flask之扩展支持正则的路由
from flask import Flask, url_for from werkzeug.routing import BaseConverter app = Flask(import_name=__name__) # 1. 写转换器类,类名任意写,必须继承自BaseConverter class RegexConverter(BaseConverter): """ 构造方法 自定义URL匹配正则表达式 regex是传入的参数 """ def __init__(self, map, regex): super(RegexConverter, self).__init__(map) # 传入的参数只须要赋上值就行了,其余就不用管了 self.regex = regex # 类里须要实现两个方法:to_python 和 to_url def to_python(self, value): """ to_python的做用:路由匹配时,在正则匹配成功后,在传给视图函数以前,执行它,对匹配的数据进行一次校验 :param value: :return: """ return int(value) def to_url(self, value): """ to_url的做用:使用url_for反向生成URL时,传递的参数通过该方法处理,返回的值用于生成URL中的参数 :param value: :return: """ val = super(RegexConverter, self).to_url(value) return val # 2. 将转换器类RegexConverter 添加到flask的转换器列表,即要添加到 DEFAULT_CONVERTERS
app.url_map.converters['regex'] = RegexConverter
# regex代指的就是RegexConverter类,而后加(),
# 就表示会把括号里的正则表达式\d+看成参数传递给类RegexConverter的第二个参数regex
@app.route('/index/<regex("\d+"):nid>')
def index(nid): print(url_for('index', nid='888')) # url_for时,先执行to_url
return 'Index' if __name__ == '__main__': app.run()
1. 模板的使用
Flask使用的时Jinja2模板,因此其语法和Django无差异
2.自定义模板方法
Flask中自定义模板方法的方式和Bottle类似,建立一个函数并经过参数的形式传入render_template.
前面的示例也涉及到了模板,下面再看个简单的示例,传入函数到模板里
from flask import Flask,render_template app = Flask(__name__) app.debug = True def func1(arg): return "hello" + arg @app.route("/index") def index(): return render_template("s5index.html",f=func1) # 传入函数 if __name__=="__main__": app.run()
S5index.html页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 传入函数 --> <h1>{{f('小马达')}}</h1> </body> </html>
from flask import Flask,render_template app = Flask(__name__) app.debug = True def func1(arg): return "<input type='text' value=%s />"%(arg,) @app.route("/index") def index(): return render_template("s5index.html",f=func1) # 传入函数 if __name__=="__main__": app.run()
s5index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 传入函数 --> {{f('小马达')}} </body> </html>
运行:http://127.0.0.1:5000/index
运行结果:
能输出这样,就是很好的,由于它是作了防止xss攻击了
要想让他免除这种设置,Django里能够再前端模板里经过管道符safe |safe, 后台也能够marksafe,同理,Flask也能够
(1)Flask前面模板里经过 |safe 作
s5index.html模板前端
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- |safe 防xss攻击 --> {{f('小马达')|safe}} </body> </html>
运行结果:
(2)Flask后端经过 Markup作
后端经过Markup作
from flask import Flask,render_template,Markup app = Flask(__name__) app.debug = True def func1(arg): return Markup("<input type='text' value=%s />"%(arg,)) @app.route("/index") def index(): return render_template("s5index.html",f=func1) # 传入函数 if __name__=="__main__": app.run()
s5index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 传入函数 --> {{f('小马达')}} </body> </html>
运行结果:
(3)宏定义, 在html里定义, 经过关键字 macro关键字定义
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 传入函数 --> {{f('小马达')}} <!-- 宏定义 做用:就是定义一块html,至关于就是一个函数 xx是函数名 --> {% macro xx(name,type='text',value='') %} <input type="{{ type }}" name="{{ name }}1" value="{{value}}"> <input type="{{ type }}" name="{{ name }}2" value="{{value}}"> <input type="{{ type }}" name="{{ name }}3" value="{{value}}"> <input type="{{ type }}" name="{{ name }}4" value="{{value}}"> {% endmacro %} <!--执行,n是name --> {{ xx('n') }} </body> </html>
运行结果:
from flask import Flask,request,render_template,redirect,jsonify,make_response app = Flask(__name__) @app.route('/login.html', methods=['GET', "POST"]) def login(): return "内容" # 请求相关信息 # 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 "字符串" # return render_template('html模板路径',**{}) # return redirect('/index.html') # return jsonify({'k':'v'}) # 若是想设置响应头和回显cookie,就须要用到make_response # response = make_response(render_template('index.html')) # response = make_response("字符串") # response是flask.wrappers.Response类型 # response.delete_cookie('key') # response.set_cookie('key', 'value') # response.headers['X-Something'] = 'A value' # return response if __name__ == '__main__': app.run() ''' 示例 def index(): response = make_response("字符串") response.set_cookie response.delete_cookie('key') response.headers['X-Something'] = 'A value' return response '''
除请求对象以外,还有一个 session 对象。它容许你在不一样请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,而且对 Cookies 进行密钥签名要使用会话,你须要设置一个密钥。
设置:session['username'] = 'xxx'
from flask import Flask,session app=Flask(__name__) app.debug = True # session 在使用以前,必需要有一个secret_key,用来作签名和加密 # 记住: # 最终到浏览器的时候会有一个随机字符串,还会有k1:v1,k2:v2序列化以后,再用secret_key加密签名以后, # 返回到浏览器上的 app.secret_key="dfjade890sddss" app.session_interface app.route("/") def index(): # session['k1'] = 'v1'写法很像字典,由于它就是一个继承了字典类的对象 # flask内置的使用加密cookie(签名cookie)来保存数据 session['k1'] = 'v1' session['k2'] = 'v2' # 删除session session.pop('k1') return 'xxx' if __name__=="__main__": app.run()
pip3 install Flask-Session run.py from flask import Flask from flask import session from pro_flask.utils.session import MySessionInterface app = Flask(__name__) app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' app.session_interface = MySessionInterface() @app.route('/login.html', methods=['GET', "POST"]) def login(): print(session) session['user1'] = 'alex' session['user2'] = 'alex' del session['user2'] return "内容" if __name__ == '__main__': app.run() session.py #!/usr/bin/env python # -*- coding:utf-8 -*- import uuid import json from flask.sessions import SessionInterface from flask.sessions import SessionMixin from itsdangerous import Signer, BadSignature, want_bytes class MySession(dict, SessionMixin): def __init__(self, initial=None, sid=None): self.sid = sid self.initial = initial super(MySession, self).__init__(initial or ()) def __setitem__(self, key, value): super(MySession, self).__setitem__(key, value) def __getitem__(self, item): return super(MySession, self).__getitem__(item) def __delitem__(self, key): super(MySession, self).__delitem__(key) class MySessionInterface(SessionInterface): session_class = MySession container = {} def __init__(self): import redis self.redis = redis.Redis() def _generate_sid(self): return str(uuid.uuid4()) def _get_signer(self, app): if not app.secret_key: return None return Signer(app.secret_key, salt='flask-session', key_derivation='hmac') def open_session(self, app, request): """ 程序刚启动时执行,须要返回一个session对象 """ sid = request.cookies.get(app.session_cookie_name) if not sid: sid = self._generate_sid() return self.session_class(sid=sid) signer = self._get_signer(app) try: sid_as_bytes = signer.unsign(sid) sid = sid_as_bytes.decode() except BadSignature: sid = self._generate_sid() return self.session_class(sid=sid) # session保存在redis中 # val = self.redis.get(sid) # session保存在内存中 val = self.container.get(sid) if val is not None: try: data = json.loads(val) return self.session_class(data, sid=sid) except: return self.session_class(sid=sid) return self.session_class(sid=sid) def save_session(self, app, session, response): """ 程序结束前执行,能够保存session中全部的值 如: 保存到resit 写入到用户cookie """ domain = self.get_cookie_domain(app) path = self.get_cookie_path(app) httponly = self.get_cookie_httponly(app) secure = self.get_cookie_secure(app) expires = self.get_expiration_time(app, session) val = json.dumps(dict(session)) # session保存在redis中 # self.redis.setex(name=session.sid, value=val, time=app.permanent_session_lifetime) # session保存在内存中 self.container.setdefault(session.sid, val) session_id = self._get_signer(app).sign(want_bytes(session.sid)) response.set_cookie(app.session_cookie_name, session_id, expires=expires, httponly=httponly, domain=domain, path=path, secure=secure)
真正用的时候,更多经常使用的是第三方库 Flask_session
#!/usr/bin/env python # -*- coding:utf-8 -*- """ pip3 install redis pip3 install flask-session """ from flask import Flask, session, redirect from flask.ext.session import Session app = Flask(__name__) app.debug = True app.secret_key = 'asdfasdfasd' app.config['SESSION_TYPE'] = 'redis' from redis import Redis app.config['SESSION_REDIS'] = Redis(host='192.168.0.94',port='6379') Session(app) @app.route('/login') def login(): session['username'] = 'alex' return redirect('/index') @app.route('/index') def index(): name = session['username'] return name if __name__ == '__main__': app.run() 第三方session