Flask02-从 Hello World 开始
Flask程序运行过程
- 所有Flask程序必须有一个程序实例。
- 当客户端想要获取资源时,一般会通过浏览器发起HTTP请求。
- 此时,Web服务器使用WSGI(Web Server Gateway Interface)协议,把来自客户端的所有请求都交给Flask程序实例,程序实例使用Werkzeug来做路由分发(URL请求和视图函数之间的对应关系)。
- 根据每个URL请求,找到具体的视图函数并进行调用。
- 在Flask程序中,路由的实现一般是通过程序实例的装饰器实现。
- Flask调用视图函数后,可以返回两种内容:
- 字符串内容:将视图函数的返回值作为响应的内容,返回给客户端(浏览器)
- HTML模版内容:获取到数据后,把数据传入HTML模板文件中,模板引擎负责渲染HTTP响应数据,然后返回响应数据给客户端(浏览器)
示例:
- 新建文件hello.py
- 导入Flask类
- Flask函数接收一个参数name,它会指向程序所在的模块
- 装饰器的作用是将路由映射到视图函数index
- Flask应用程序实例的run方法启动WEB服务器
# -*- coding:utf-8 -*-
from flask import Flask, render_template
# 第一个参数:表示当前应用运行的模块名字,并且可以决定从哪里查找静态文件
# 如果传入的模块名字不存在的,会从当前应用运行的模块查找静态文件
# 如果传入的模块名字是存在的,会从传入的模块名中加载静态文件
app = Flask(__name__,
static_url_path='/python', # 指定静态文件加载时url的前缀
static_folder='static', # 指定静态文件存放我文件夹
template_folder='templates') # 指定模板文件存储的文件夹,《默认从项目的根路径查找模板》
@app.route('/')
def index():
# return 'index'
# 响应模板
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True, port=5001, host='192.168.72.58')
- 在程序运行过程中,程序实例中会使用
url_map
将装饰器路由和视图的对应关系保存起来,打印结果如下图:
配置参数
- app.config.from_pyfile() # 从配置文件中加载
- app.config.from_object() # 从对象中加载
- app.config.from_envvar() # 从环境变量中加载# -*- coding:utf-8 -*-
# 导入Flask from flask import Flask class Config(object): """加载配置参数的类 等价于django中的setting""" # 开启调试模式 DEBUG = True # 创建Falsk应用实例 app = Flask(__name__) # 加载配置:从对象中加载配置参数 # app.config.from_object(Config) # 加载配置:从配置文件中加载配置参数 # app.config.from_pyfile('config.conf') # 加载配置:从环境变量中加载配置参数 # app.config.from_envvar('envvar_config') # app.config['DEBUG'] = True app.debug = True # 定义视图函数,并绑定路由 @app.route('/') def index(): # 演示异常 a = 10 / 0 return 'index'
# 启动该应用的入口 if __name__ == '__main__': # 启动应用 app.run()
读取配置参数
- app.config.get()
- 在视图中 current_app.config.get()
app.run的参数
- app.run(host=”0.0.0.0”, port=5000, debug = True)
路由的各种定义方式
# -*- coding:utf-8 -*- from flask import Flask, request app = Flask(__name__) # @app.route('/', methods=['GET', 'POST']) def index(): method = request.method return 'index %s' % method @app.route('/index2') def index2(): return 'index2' if __name__ == '__main__': # 打印路由和视图的映射关系 print app.url_map app.run(debug=True)
给路由传参示例
有时我们需要将同一类URL映射到同一个视图函数处理,比如:使用同一个视图函数 来显示不同用户的个人信息。
路由传递的参数默认当做string处理,这里指定int,尖括号中的内容是动态的,也可不指定类型
@app.route('/user/<int:id>') def hello_itheima(id): return 'hello world %d' %id
重定向redirect示例,反向解析,重新定向穿参
# -*- coding:utf-8 -*- from flask import Flask, redirect, url_for app = Flask(__name__) @app.route('/') def index(): return 'index' @app.route('/demo1') def demo1(): # 重定向到外网 # return redirect('http://www.baidu.com') # 重定向到内部路由 # return redirect('/') # 重定向使用反向解析 传入视图名字: url_for('index') == '/' return redirect(url_for('index')) # 以下代码演示重定向并传参 @app.route('/order/<int:order_id>') def order(order_id): return 'order %s' % order_id @app.route('/demo2') def demo2(): return redirect(url_for('order', order_id='123')) # url_for第一个参数为路由名字,第二个为参数名字 if __name__ == '__main__': print app.url_map app.run(debug=True)
路由转换器:BaseConverter &正则
基础版本:
# -*- coding:utf-8 -*- from flask import Flask, redirect, url_for from werkzeug.routing import BaseConverter class RegexConverter(BaseConverter): """自定义路由正则转换器:实现order_id必须是三位数字""" # 该类属性用于匹配order_id regex = r'\d{3}' app = Flask(__name__) # 将自定义的路由正则转换器,添加到默认的转换器列表中 app.url_map.converters['re'] = RegexConverter @app.route('/') def index(): return 'index' @app.route('/') def index2(): return 'index2' # 需求: order_id必须是三位数字 @app.route('/order/<re:order_id>') def order(order_id): print type(order_id) return 'order %s' % order_id @app.route('/demo') def demo(): return redirect(url_for('order', order_id='123')) if __name__ == '__main__': print app.url_map app.run(debug=True)
升级版本:
# -*- coding:utf-8 -*- from flask import Flask, redirect, url_for from werkzeug.routing import BaseConverter class RegexConverter(BaseConverter): """实现外界传入什么正则,我就匹配什么条件""" def __init__(self, url_map, *args): super(RegexConverter, self).__init__(url_map) self.regex = args[0] # 机制:在匹配成功之后,MapAdapter执行匹配和调用视图函数,调用视图函数之前,可以得到要匹配的参数 # 作用:可以在这个方法中,对要匹配的参数进行进一步的处理,比如转换数据类型 def to_python(self, value): value = int(value) return value # 需要搭配url_for使用的 # 机制:在匹配成功之前,在视图函数调用之前,获取要匹配的参数 # 作用:可以在这个方法中,对要匹配的参数进行第一次的处理,比如对不合格的参数进行校正或者修复,尽量满足正则 # 保证能够顺利的进入到to_python # 只要能够进入到to_python就说明匹配成功,匹配成功视图函数才会调用 def to_url(self, value): return value app = Flask(__name__) # 将自定义的路由转换器的类添加到默认的转换器列表中 app.url_map.converters['re'] = RegexConverter @app.route('/') def index(): return 'index' @app.route('/order/<re("\d{3}"):order_id>') def order(order_id): return 'order %s' % order_id @app.route('/demo') def demo(): return redirect(url_for('order', order_id='12')) if __name__ == '__main__': print app.url_map app.run(debug=True)
defultconverter.py
# -*- coding:utf-8 -*- from flask import Flask app = Flask(__name__) @app.route('/<any("hehe, help, imprint, class, "foo,bar""):page_name>') def any(page_name): return 'page_name %s' % page_name @app.route('/<uuid:uuid_num>') def test_uuid(uuid_num): return 'uuid %s' % uuid_num if __name__ == '__main__': app.run(debug=True)
返回JSON
# -*- coding:utf-8 -*- from flask import Flask, json, jsonify app = Flask(__name__) @app.route('/json2') def json2(): # 定义字典:方便转json json_dict = { 'name':'zxc', 'age':18 } # Content-Type: application/json # 工作中使用的 return jsonify(json_dict) @app.route('/json1') def json1(): # 定义字典:方便转json json_dict = { 'name':'zxc', 'age':18 } # Content-Type: text/html; charset=utf-8 return json.dumps(json_dict) if __name__ == '__main__': app.run(debug=True)
状态码
- 直接return
- 可以自定义返回状态码,可以实现不符合http协议的状态码,例如:error=666,errmsg='查询数据库异常',其作用是为了实现前后端数据交互的方便
- abort方法
- 只会抛出符合http协议的异常状态码,用于手动抛出异常
自定义响应头和状态码
# -*- coding:utf-8 -*- from flask import Flask, abort app = Flask(__name__) @app.route('/') def index(): # 演示异常 a = 10 / 0 return 'index', 666, {'name':'zxj'} @app.route('/demo1') def demo1(): abort(404) @app.errorhandler(404) def demo2(e): """专门捕获指定的状态码异常信息,然后处理异常""" print e return '服务器搬家了' @app.errorhandler(ZeroDivisionError) def demo3(e): """专门捕获指定的异常信息,然后处理异常""" print e return '写这个代码的程序员已经祭天了' if __name__ == '__main__': app.run(debug=True)
request.py
# -*- coding:utf-8 -*- from flask import Flask, request app = Flask(__name__) # http://127.0.0.1/upload?name=zxc&age=18 @app.route('/upload', methods=['POST']) def upload(): print request.url, request.method # 获取查询字符串 name = request.args.get('name') age = request.args.get('age') # 演示表单数据的读取 pic = request.files.get('pic') pic.save('./123.jpg') return 'index %s %s' % (name, age) # http://127.0.0.1/index1?name=zxc&age=18 @app.route('/index1', methods=['POST']) def index1(): print request.url, request.method # 获取查询字符串 name = request.args.get('name') age = request.args.get('age') # 演示表单数据的读取 w = request.form.get('w') return 'index %s %s %s' % (name, age, w) # http://127.0.0.1/?name=zxc&age=18 @app.route('/') def index(): print request.url, request.method # 获取查询字符串 name = request.args.get('name') age = request.args.get('age') return 'index %s %s' % (name,age) if __name__ == '__main__': app.run(debug=True)
hook.oy
# -*- coding:utf-8 -*- from flask import Flask app = Flask(__name__) @app.route('/') def index(): print 'index' return 'index' # 第一次请求开始前调用的,还没有执行请求 # 作用:可以做一些初始化的操作。比如连接到数据库 @app.before_first_request def demo1(): print 'demo1' # 请求开始执行前调用的 # 对请求做一些校验操作,如果请求信息有异常,或者发现自己被攻击,可以在这里直接返回,不会进入到视图函数中 @app.before_request def demo2(): print 'demo2' # 请求结束之后调用的、视图函数执行结束后调用的 # 可以接受视图函数执行之后的响应结果 # 可以对响应进行统一的处理,比如在响应中统一设置cookie或者响应头信息 @app.after_request def demo3(response): print 'demo3' # 设置响应头 response.headers['Content-Type'] = 'application/json' return response # 捕获到服务器异常后调用的 # 可以接受异常信息,处理异常信息 @app.teardown_request def demo4(e): print 'demo4', e if __name__ == '__main__': app.run(debug=True)
cookie.py
# -*- coding:utf-8 -*- from flask import Flask, make_response app = Flask(__name__) @app.route('/set_cookie') def set_cookie(): # 创建response对象 response = make_response('index') # 使用response对象写入cookie到浏览器,会话结束就过期 response.set_cookie('name','zxc') # 设置过期时间,单位是秒 response.set_cookie('age', '18', max_age=3600) return response @app.route('/del_cookie') def del_cookie(): response = make_response('del_cookie') response.delete_cookie('name') return response if __name__ == '__main__': app.run(debug=True)
session.py
# -*- coding:utf-8 -*- from flask import Flask, session app = Flask(__name__) # 配置秘钥参数 app.config['SECRET_KEY'] = 'n%lq5-oyhv0not14+xv4m=q-@^kt1ptzst5)w$1@o*)=ose&nv' @app.route('/set_session') def set_session(): # 设置session
# 设置session有效时间 session.permanent = True
session.permanent_session_lifetime=3600 session['name'] = 'zxc' # 读取session name = session['name'] return 'set_session' @app.route('/del_session') def del_session(): # 设置session session.pop('name') return 'del_session' if __name__ == '__main__': app.run(debug=True)
上下文context.py
# -*- coding:utf-8 -*- from flask import Flask, request, session, current_app, g app = Flask(__name__) app.config['DEBUG'] = True # 请求上下文:是在请求生成以后才可以直接使用的,访问路由时才有请求,如果写在这里,还没有请求发生但是已经被编译了 # 请求上下文超出了工作范围 # RuntimeError: working outside of request context # print request.method # print session.get('name') # 应用上下文:只有当应用启动后才能使用,保存了一些配置参数信息 # 应用上下文超出了工作范围 # RuntimeError: working outside of application context # print current_app.config['DEBUG'] @app.route('/') def index(): # 请求上下文使用:一般在视图中使用,因为视图会执行,请求一定有 print request.method print session.get('name') # 应用上下文使用,能进入视图,程序一定启动了 print current_app.config['DEBUG'] return 'index' if __name__ == '__main__': # 在这里只是启动应用,只有当这个方法run()走完,我们的应用才算启动,也是超出了应用上下文的使用范围 # print current_app.config['DEBUG'] app.run(debug=True)
script.py
# -*- coding:utf-8 -*- from flask import Flask # 1.导入脚本管理类 from flask_script import Manager app = Flask(__name__) # 2.实例化脚本管理器 manager = Manager(app) # 4.自定义脚本:将来会使用脚本完成数据库的迁移 @manager.command def test_custom_script(): print u'这里实在测试自定义脚本,实现自己的逻辑' @app.route('/') def index(): a = 10 / 0 return 'index' if __name__ == '__main__': # app.run(debug=True) # 3.使用脚本管理器启动 runserver manager.run()
template.py
# -*- coding:utf-8 -*- from flask import Flask, render_template app = Flask(__name__) @app.route('/') def index(): my_list = [1,2,3,4,5,6,7] my_int = 10 my_dict = { 'name':'zxj', 'age':'2' } # 上下文 context = { 'my_list':my_list, 'my_int':my_int, 'my_dict':my_dict } # **context : 将字典展开(模板中的自动提示没有了) return render_template('test01_template.html', **context) # return render_template('test01_template.html', context=context) # return render_template('test01_template.html', my_list=my_list, my_int=my_int, my_dict=my_dict) if __name__ == '__main__': app.run(debug=True)
filter.py
# -*- coding:utf-8 -*- from flask import Flask, render_template app = Flask(__name__) @app.route('/') def index(): my_list = [1,2,3,4,5,6,7] my_int = 10 my_dict = { 'name':'zxj', 'age':2 } my_list2 = [my_dict,my_dict] # 上下文 context = { 'my_list':my_list, 'my_int':my_int, 'my_dict':my_dict, 'my_list2':my_list2 } # **context : 将字典展开(模板中的自动提示没有了) return render_template('test02_filter.html', **context) # 自定义过滤器方式一 : 使用装饰器 # @app.template_filter('listReverse') def do_listReverse(list_var): """自定义反转列表的过滤器""" # 注意 : 使用中间变量暂时接受原始数据,为了操作的过程中不影响原始的数据 temp_list = list_var # 注意: reverse() 没有返回值,不要直接return reverse() temp_list.reverse() return temp_list # 自定义过滤器的方式二:把指定的函数添加到默认的过滤器容器中 app.add_template_filter(do_listReverse, 'listReverse') if __name__ == '__main__': app.run(debug=True)