01 flask 基本知识
中文文档(http://docs.jinkan.org/docs/flask/)
英文文档(http://flask.pocoo.org/docs/0.11/)
requirements 文件
在虚拟环境使用以下命令将当前虚拟环境中的依赖包以版本号生成至文件中:
pip freeze >requirements.txt
需求文件的内容示例如下:
alembic==0.9.2 blinker==1.4 click==6.7 dominate==2.3.1 Flask==0.10.1 Flask-Bootstrap==3.3.7.1 Flask-Login==0.4.0 Flask-Mail==0.9.1 Flask-Migrate==2.0.4 Flask-Redis==0.3.0 Flask-Script==2.0.5 Flask-SQLAlchemy==2.2 Flask-Testing==0.6.2 Flask-WTF==0.14.2 gunicorn==19.7.1 itsdangerous==0.24 Jinja2==2.9.6 Mako==1.0.6 MarkupSafe==1.0 MySQL-python==1.2.5 mysqlclient==1.3.10 pkg-resources==0.0.0 PyMySQL==0.7.11 python-dateutil==2.6.0 python-editor==1.0.3 redis==2.10.5 six==1.10.0 SQLAlchemy==1.1.10 uWSGI==2.0.15 visitor==0.1.3 Werkzeug==0.12.2 WTForms==2.1 xmltodict==0.11.0
当需要创建这个虚拟环境的完全副本,可以创建一个新的虚拟环境,并在其上运行以下命令:
pip install -r requirements.txt
入门小案例
Flask程序运行过程
- 所有Flask程序必须有一个程序实例。
- 当客户端想要获取资源时,一般会通过浏览器发起HTTP请求。
- 此时,Web服务器使用WSGI(Web Server Gateway Interface)协议,把来自客户端的所有请求都交给Flask程序实例,程序实例使用Werkzeug来做路由分发(URL请求和视图函数之间的对应关系)。
- 根据每个URL请求,找到具体的视图函数并进行调用。
- 在Flask程序中,路由的实现一般是通过程序实例的装饰器实现。
- Flask调用视图函数后,可以返回两种内容:
- 字符串内容:将视图函数的返回值作为响应的内容,返回给客户端(浏览器)
- HTML模版内容:获取到数据后,把数据传入HTML模板文件中,模板引擎负责渲染HTTP响应数据,然后返回响应数据给客户端(浏览器)
# coding:utf8 from flask import Flask # 创建一个flask应用程序 app = Flask(__name__) @app.route("/") def index(): return "hello" if __name__ == '__main__': # 输出当前应用所有的路由映射 print app.url_map # 运行应用程序 app.run()
运行程序:
- 在程序运行过程中,程序实例中会使用
url_map将装饰器路由和视图的对应关系保存起来,打印结果如下图:

在地址栏中输入
http://127.0.0.1:5000/

路由的各种定义方式
请求方式限定,使用 methods 参数指定可接受的请求方式,可以是多种,method= ["POST","GET"]
# coding:utf8 from flask import Flask # 创建一个应用对象 app = Flask(__name__) @app.route("/", methods=["POST"]) def index(): return "hello word" if __name__ == '__main__': app.run()
运行程序
路由查找方式
同一路由指向两个不同的函数,在匹配过程中,至上而下依次匹配
from flask import Flask app = Flask(__name__) @app.route('/index') def index1(): return "hello" @app.route('/index') def index2(): return "hi" if __name__ == '__main__': print app.url_map app.run(debug=True)

给路由传参示例
有时我们需要将同一类URL映射到同一个视图函数处理,比如:使用同一个视图函数 来显示不同用户的个人信息。
路由传递的参数默认当做string处理,也可不指定类型,尖括号中的内容是动态的。指定int等其他的格式,具有限制参数的作用
通过一对尖括号获取传递的参数
app.run(debug=True) 可以实现修改后自动保存的功能
字符串要出现中文的时候加个u“”
return u"接受的参数是 %s" % user_id
默认的参数类型
# coding:utf8 from flask import Flask app = Flask(__name__) @app.route("/user/<user_id>") def demo(user_id): return u"接受的参数是 %s" % user_id if __name__ == '__main__': app.run(debug=True)

限制参数类型
@app.route("/num/<int:id>") 限制只能接受数字,接受其他的会报错
# coding:utf8 from flask import Flask app = Flask(__name__) @app.route("/num/<int:id>") def demo1(id): return u"接受到的参数是%s" % id if __name__ == '__main__': app.run(debug=True)

重定向redirect示例
from flask import Flask,redirect
return redirect('跳转的路径')
# coding:utf8 from flask import Flask,redirect app = Flask(__name__) @app.route('/') def deomo(): return redirect('http://www.baidu.com') if __name__ == '__main__': app.run(debug=True)

返回JSON
from flask import Flask,json
from flask import Flask,json app = Flask(__name__) @app.route("/") def do_json(): strs = {"name":"zhang"} return json.dumps(strs) if __name__ == '__main__': app.run(debug=True)

通过查看响应头可以看出,返回的格式并不json的格式。
这时我们可以使用Flask的另一个json模块
from flask import Flask,jsonify
from flask import Flask,jsonify app = Flask(__name__) @app.route("/") def do_json(): strs = {"name": "zhang"} return jsonify(strs) if __name__ == '__main__': app.run(debug=True)

正则路由示例
在web开发中,可能会出现限制用户访问规则的场景,那么这个时候就需要用到正则匹配,限制访问,优化访问
导入转换包
from werkzeug.routing import BaseConverter
Flask中6个自带的转换器
Flask中的正则匹配的实质就是在其内部有转换器帮助我们匹配的
我们要想自定义我们自己的正则,所以就需要重构 BaseConverter 把我们自定义的正则,添加到路由转换器列表中 app.url_map.converters[ " 自定义转换器的名字"] = 自定义的转换器类
总结,自定义一个转换器的步奏:
1 导包 from werkzeug.routing import BaseConverter
2 自定义一个类,继承BaseConverter
3 重够父类中的init方法,把父类的匹配规则改写成自己的
4 把自定义的转换器添加到,路由转换列表中
5 可以按照上面提到的限制参数的使用方法,使用自定义的转换器
# coding:utf8 from flask import Flask from werkzeug.routing import BaseConverter class MyConverter(BaseConverter): # 重构父类的init方法 def __init__(self,map,*args): super(MyConverter, self).__init__(map) # 覆盖掉父类的匹配规则 self.regex = args[0] app = Flask(__name__) # 把自定义的转换器类添加到路由转换器列表中 app.url_map.converters["MyConverter"] = MyConverter # 按照限制参数的方式使用自定义的转换器 @app.route('/regl/<MyConverter("[0-9]{4}"):id>') def reg(id): return u"正则匹配的结果 %s" %id if __name__ == '__main__': app.run()
对上面的局部代码进行再次解释
<MyConverter("[0-9]{4}")
上面的代码,就是实例化一个对象,把匹配的规则当参数去覆盖掉父类的匹配规则
Flask底层自带的转化器的使用
any(),转换器的使用
匹配其中的一项。项目可以是Python标识符或字符串
# coding:utf8 # flask内部的转换器的使用 from flask import Flask app = Flask(__name__) #any @app.route("/any/<any(about, help,'nihao'):id>") def router(id): return u"any匹配到的结果是:%s" % id if __name__ == '__main__': app.run(debug=True)

path() 匹配/后面所有的东西
# coding:utf8 # flask内部的转换器的使用 from flask import Flask app = Flask(__name__) #path @app.route('/<path:wikipage>') def router1(wikipage): return wikipage if __name__ == '__main__': app.run(debug=True)

返回状态码示例
在 Python 中返回状态码有两种方式实现:
- 直接return - 可以自定义返回状态码,可以实现不符合http协议的状态码,例如:error=666,errmsg='查询数据库异常',其作用是为了实现前后端数据交互的方便 - abort方法 - 只会抛出符合http协议的异常状态码,用于手动抛出异常
通过return 返回状态码
from flask import Flask app = Flask(__name__) @app.route('/statecode') def demo1(): return 'statecode',666 if __name__ == '__main__': app.run(debug=True)

通过abort方法,只会抛出符合http协议的异常状态码,用于手动抛出异常
from flask import Flask, abort
# 通过abort抛出,手动抛出符合http协议的状态码 @app.route('/notfind') def demo2(): abort(404) return '抛出404的异常' if __name__ == '__main__': app.run(debug=True)

errorhandler 可以捕获http错误的状态码,还可以捕获指定的异常
注册一个错误处理程序,当程序抛出指定错误状态码的时候,就会调用该装饰器所装饰的方法
使用的方法:
@app.errorhandler(异常的状态码)
只要请求出现捕获到的状态码就会自动执行@app.errorhandler下的函数
# coding:utf8 from flask import Flask,abort app = Flask(__name__) # return抛出异常 @app.route('/statecode') def demo1(): return 'statecode',666 # 通过abort抛出,手动抛出符合http协议的状态码 @app.route('/notfind') def demo2(): abort(404) return '抛出404的异常' # errorhandler 可以捕获http错误的状态码,还可以捕获指定的异常 @app.errorhandler(404) def page_not_found(e): return u"页面不见了 %s" % e if __name__ == '__main__': app.run(debug=True)
def page_not_found(e)中的e代表的是异常的信息

请求勾子
在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:在请求开始时,建立数据库连接;在请求结束时,指定数据的交互格式。为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子。
请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:
- before_first_request:在处理第一个请求前运行。
- before_request:在每次请求前运行。
- after_request:如果没有未处理的异常抛出,在每次请求后运行。
- teardown_request:在每次请求后运行,即使有未处理的异常抛出。
4个钩子的执行顺序,
先执行before_first_request,before_request,after_request,teardown_request
# -*- coding:utf-8 -*- from flask import Flask, abort app = Flask(__name__) @app.route('/') def index(): print "要返回内容了" return 'index' # 第一次请求之前,只会执行一次,可以在此函数中做一些请求前的初始化操作 @app.before_first_request def before_first_request(): print "第1次请求之前" @app.before_request def before_request(): print "请求之前" # 如果直接在这个函数里面返回数据的话,那么真实的视图函数就不会被执行了 # 可以在此函数中做一些请求的判断 return u"哈哈" @app.after_request def after_request(response): print "请求之后" return response # 请求销毁的时候执行,一定会执行,不管服务器有没有抛出错误 ,一定会执行 @app.teardown_request def teardown_request(error): print "请求销毁之后" if __name__ == '__main__': print app.url_map app.run(debug=True)


装饰器路由的实现
Flask有两大核心:Werkzeug和Jinja2
- Werkzeug实现路由、调试和Web服务器网关接口
- Jinja2实现了模板。
Werkzeug是一个遵循WSGI协议的python函数库
- 其内部实现了很多Web框架底层的东西,比如request和response对象; - 与WSGI规范的兼容;支持Unicode; - 支持基本的会话管理和签名Cookie; - 集成URL请求路由等。
Werkzeug库的routing模块负责实现URL解析。不同的URL对应不同的视图函数,routing模块会对请求信息的URL进行解析,匹配到URL对应的视图函数,以此生成一个响应信息。
routing模块内部有:
- Rule类(用来构造不同的URL模式的对象) - Map类(存储所有的URL规则) - BaseConverter的子类(负责定义匹配规则) - MapAdapter类(负责具体URL匹配的工作)
状态保持
因为http是一种无状态协议,不会保持某一次请求所产生的信息,如果想实现状态保持,在开发中解决方式有:
- cookie:数据存储在客户端,节省服务器空间,但是不安全
- session:会话,数据存储在服务器端
无状态协议:
协议对于事务处理没有记忆能力
对同一个url请求没有上下文关系
每次的请求都是独立的,它的执行情况和结果与前面的请求和之后的请求是无直接关系的,它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况
服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器
人生若只如初见
设置cookie
#coding:utf8 from flask import Flask,make_response,request app = Flask(__name__) @app.route('/cookie') def set_cookie(): response = make_response('cookie设置成功') response.set_cookie("name", 'zhang') return response if __name__ == '__main__': app.run(debug=True)

获取cookie
#coding:utf8 from flask import Flask,make_response,request app = Flask(__name__) @app.route('/cookie') def set_cookie(): response = make_response('cookie设置成功') response.set_cookie("name", 'zhang') return response @app.route('/getcookie') def get_cookie(): res = request.cookies.get("name") return res if __name__ == '__main__': app.run(debug=True)

session数据的获取
记得设置secret_key: app.secret_key = 'itheima' secret_key的作用:https://segmentfault.com/q/1010000007295395
设置session
from flask import Flask, session # 注意设置session的时候必须设置app.secret_key,值随便写 app.secret_key = "aaa" session['username'] = 'itcast'
获取session
session.get('键')
# coding:utf8 from flask import Flask, session, redirect, url_for app = Flask(__name__) # 注意设置session的时候必须设置app.secret_key app.secret_key = "aaa" @app.route('/index1') def index1(): session['username'] = 'itcast' return redirect(url_for('index')) @app.route('/') def index(): return session.get('username') if __name__ == '__main__': app.run(debug=True)

url_for的使用
from flask import Flask, redirect, url_for redirect(url_for('index'))
url_for("参数是函数"),执行的是函数
上下文
上下文:相当于一个容器,保存了Flask程序运行过程中的一些信息。
Flask中有两种上下文,请求上下文和应用上下文。
请求上下文(request context)
Flask从客户端收到请求时,要让视图函数能访问一些对象,这样才能处理请求。请求对象是一个很好的例子,它封装了客户端发送的HTTP请求。
要想让视图函数能够访问请求对象,一个显而易见的方式是将其作为参数传入视图函数,不过这会导致程序中的每个视图函数都增加一个参数,除了访问请求对象,如果视图函数在处理请求时还要访问其他对象,情况会变得更糟。为了避免大量可有可无的参数把视图函数弄得一团糟,Flask使用上下文临时把某些对象变为全局可访问。
- request 和 session 都属于请求上下文对象。
- request:封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get('user'),获取的是get请求的参数。
- session:用来记录请求会话中的信息,针对的是用户信息。举例:session['name'] = user.id,可以记录用户信息。还可以通过session.get('name')获取用户信息。
- 当调用app = Flask(name)的时候,创建了程序应用对象app;
- request 在每次http请求发生时,WSGI server调Flask.call();然后在Flask内部创建的request对象;
- app的生命周期大于request,一个app存活期间,可能发生多次http请求,所以就会有多个request。
- 最终传入视图函数,通过return、redirect或render_template生成response对象,返回给客户端。
应用上下文(application context)
它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的。
应用上下文对象有:current_app,g
current_app
应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:
- 应用的启动脚本是哪个文件,启动时指定了哪些参数
- 加载了哪些配置文件,导入了哪些配置
- 连了哪个数据库
- 有哪些public的工具类、常量
- 应用跑再哪个机器上,IP多少,内存多大
current_app.name current_app.test_value='value'
g变量
g作为flask程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据,g保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别
g.name='abc'
两者区别:
- 请求上下文:保存了客户端和服务器交互的数据
- 应用上下文:flask 应用程序运行过程中,保存的一些配置信息,比如程序名、数据库连接、应用信息等
请求上下文和应用上下文原理实现:https://segmentfault.com/a/1190000004223296
g 变量主要是保存值的
# -*- coding:utf-8 -*- from flask import Flask, make_response, session,g app = Flask(__name__) app.secret_key = "aaa" def login_required(f): def wrapper(*args, **kwargs): g.user_id = "18" return f(*args, **kwargs) return wrapper @app.route('/') @login_required def index(): user_id = g.user_id return user_id if __name__ == '__main__': app.run(debug=True)


浙公网安备 33010602011771号