1.目标
- 为什么学习框架?
- IT哲学-挨踢哲学
- 现有问题/需求,再有解决工具,工具是为解决问题/需求而诞生的
- 故学习重点是探究问题的本质,及解决问题的思路和思想
- 可能没有最好的工具,只有更合适的工具
- 学习目标
- 明确问题/需求及其背景(即工具的应用场景)
- 理解并掌握解决问题的思路
- 理解后熟练工具
- 学习方法
- 不机械的抄代码,背代码,锻炼自己写代码的能力
- 先思考,在动手
- 先想清楚问题是什么,在想请解决问题思路,最后动手将思路实现
- 多思考,勤动手,多问自己为什么,多提出不同的解决方式在动手验证可行性,多调bug
- 框架的学习目标
- 如何编写试图
- 如何处理请求
- 如何构造响应
2.Flask初识
flask 诞生于2010年,是Armin ronacher用python语言基于Werkzeug工具箱编写的轻量级web开发框架 Flask本身相当于一个内核,其他几乎所有功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login,数据库Flask-SQLAlchemy),都需要用到第三方的扩展来实现。比如可以用Flask扩展加入ORM,窗体验证工具,文件上传,身份验证等。Flask没有默认使用的数据库,可以选择Mysql,也可以使用NoSQL. 其WSGI工具箱采用Werkzeug(路由模块),模板引擎则使用Jinja2,这两个也是Flask框架的核心
- 框架对比
- 框架轻重
- 重量级框架:为方便业务程序的开发,提供了丰富的工具,组件如Django
- 轻量级框架:只提供web框架的核心功能,自由,灵活,高度定制,如Flask,Tronado
- 与Django对比
- Django提供了(Flask都没有):
- django-admin快创建项目工程目录
- manage-py 管理项目工程
- orm模型(数据库抽象层)
- admin后台管理站点
- 缓存机制
- 文件存储系统
- 用户认证系统
- Flask常用扩展包
- 扩展列表
- Flask-SQLalchemy:操作数据库
- Flask-script:插入脚本
- Flask-migrate:管理迁移数据库
- Flask-Session:Session存储方式指定
- Flask-WTF:表单
- Flask-Mail:邮件;
- Flak-Bable:提供国际化和本地化支持,翻译
- Flask-Login:认证用户的状态
- Flask-OpenID:认证
- Flask-RESTful:开发REST API的工具
- Flask-Bootstrap:集成前端Twitter Bootstrap框架
- Flask-Moment:本地化日期和时间
- Flask-Admin:简单而可扩展的管理接口的框架
- 框架选择
- 自由、灵活、高度定制–》flask
- 快速实现业务,不考虑技术类型,越简单直接越好–》Django
3.工程搭建
- 环境安装
- 虚拟环境和pip命令
- 虚拟环境(virtualenv)
- mkvirtualen 创建虚拟环境
- rmvirtualenv 删除虚拟环境
- workon +虚拟环境名 (空格 tab两次查看)进入到虚拟环境,查看所有虚拟环境
- deactivate 退出虚拟环境
- pip
- pip install 安装依赖包
- pip uninstall 卸载依赖表
- pip list 查看已安装的依赖包
- pip freeze 冻结当前环境的依赖表
- 没有virtualenv的需安装
- sudo apt-get install python3-virtualenv
- 创建虚拟环境需要联网
- mkvirtualenv flask -p /usr/bin/python3
- 安装flask pip install flask
- flask程序编写
1. 创建hellow.py文件
from flask import Flask
app=Flask(__name__)
@app.route('/')
def index():
return' hello_world'
if __name__=='__main__':
app.run()
- 启动 python helloworld.py
4.参数说明
Flask对象的初始化参数 应用程序的配置参数 app.run() 运行参数
- Flask对象初始化参数
- Flask程序实例在创建的时候,需要默认传入当前的Flask程序指定的包(模块),
- import_name
- Flask程序所在的包(模块),传__name__就可以
- 其可以决定Flask在访问静态文件时查找的路径
- static_url_path
- 静态文件访问路径,可以不传,默认为:/+static_folder
- static_folder
- 静态文件存储的文件夹,可以不传,默认为 static
- template_folder
- 模板文件存储的文件夹,可以不传,默认为 templates
- 应用程序的配置参数
- 对于Flask对象初始化参数仅仅设置的是Flask本身的属性,如“
- Flask从哪里读取静态文件 Flask从哪里读取模板文件 等等
- 应用程序配置参数设置的是一个web应用工程的相关信息如:
- 数据库的连接信息
- 日志的配置信息
- 自定义的配置信息
- 作用:集中管理项目的所有配置信息
- 使用方式:
- Django将所有配置信息都放到settings.py文件中,而Flask则不同
- Flask将配置信息保存到了app.config属性中,该属性可以按照字典类型进行操作
- 读取: app.confiig.get(name) app.config(name)
- 设置 主要使用三种方式:
- 从配置对象中加载 app.config.from_object(配置对象)
- 优点:
- 可继承–复用
- 缺点:
- 敏感数据暴露
- 应用场景:
- 默认配置
- 从配置文件中加载: app.config.from_pyfile(配置文件)
- 优点:
- 独立文件–可提取(保护敏感数据)
- 缺点:
- 不可继承
- 文件路径固定,不灵活
- 应用场景:
- 不考虑灵活性,只考虑敏感度
- 从环境变量中加载
-
环境变量一般是指操作系统中用来指定操作系统运行环境的一些参数,如临时文件夹位置和系统文件夹位置等,环境变量是在操作系统中一个具有特定名字的对象,它包含了一个或多个应用程序所将使用到的信息 -
通俗理解就是,环境变量就是我们设置在操作系统中,由操作系统代为保存的变量值 -
在linux系统中设置和读取环境变量的方式如下:
- export 变量名=变量值 #设置
- echo $变量名 #读取
-
在终端设置的环境变量在pycharm中启动不起作用,原因是终端与pycharm是两个不同的环境 -
优点:
- 独立文件
- 保护敏感数据
- 文件路径不固定,灵活
-
缺点:
- 不方便,要设置环境变量
-
项目中常用的方式
- 使用工厂模式创建Flask app,并结合使用配置对象与环境变量加载配置
- 使用环境变量加载不想出现在代码中的敏感配置信息
- 同名变量,后面的会覆盖前面的
"""
#1.配置对象方式加载配置信息
# class DefaultConfig(object):
# '''默认配置'''
# SECRET_KEY='fdkasfjowq'
#2.从配置文件中加载 setting.py
#3.从环境变量中加载配置文件
"""
app=Flask(__name__,static_url_path='/s',static_folder='static_files')
app.config.from_envvar('PROJECT_SETTING',silent=True)
@app.route('/')
def index():
print(app.config['SECRET_KEY'])
return '<a href="http://www.baidu.com">百度</a>'
"""
@app.route('/user/<name>')
def user(name):
return 'hello %s'%name
"""
if __name__ == '__main__':
app.run()
- app.run() 参数
- 可以指定运行的主机ip地址,端口,是否开启调试模式
- app.run(host='ip地址‘,port='端口号’,debug=True)
- 关于DEBUG调试模式
- 程序代码修改后可以自动重启服务器
- 在服务器出现相关错误时可以直接将错误信息返回到前端进行展示
- 开发服务器启动方式
- 在1.0版本后,由代码编写的app.run()语句调整为命令 flask.run
- 终端启动(要设置需要的环境变量):
- linux
- export FLASK_APP=flaskProject
- flask run
- windows:
- set FLASK_APP=flaskProject/app.py
- flask run
- 说明:
- 环境变量 FLASK_APP 指明flask 的启动实例
- 绑定地址 端口 flask run -h ip地址 -p 端口号
- flask run --help 获取帮助
- 生产模式与开发模式的控制
- 通过FLASK_ENV 环境变量指明
- export FLASK_ENV=production 运行在生产模式,未指明则默认为此方式
- export FLASK_ENV=development 运行在开发模式
- pycharm中运行新式flask
-
新版:换成model模式,设置Target为项目路径 -
旧版
- flask run<==>python -m falsk run
- 设置Parameters 为 -m falsk run
5.路由与蓝图
- 查询路由信息
- 命令行方式: flask routes
- 在程序中获取:
- print(app.url_map)
- 在程序中遍历路由信息:
- from rule in app.url_map.iter_rules():
. print(‘name={},path={}’.format(rule.endpoint,rule.rule)) - 通过访问 / 地址 ,以json的方式返回应用内的所有路由信息
@app.route('/')
def route_app():
'''主视图,返回所有视图网址'''
rules_iterator=app.url_map.iter_rules()
return json.dumps({rule.endpoint:rule.rule for rule in rules_iterator})
- 指定请求方式
- 在flask中,定义路由默认的请求方式为:
- GET
- OPTIONS(自带)–简化版的GET请求,用于询问服务器接口信息的,如接口允许的请求方式,允许的请求源头
- HEAD(自带)–简化版的GET请求 只返回GET请求处理的响应头,不返回响应体
- CORS 跨域 django-cors进行了中间件拦截处理了options请求
- 前端访问接口–option 进行请求询问是否允许–返回response 询问结果-- GET请求资源
- 自定义 POST PUT DELETE PATCH
- 利用methods 参数可以指定一个接口的请求方式
- 蓝图
- 初识
- 一个蓝图就是一个子应用
- 在flask应用项目中,使用蓝图Blueprint来分模块组织管理
- 蓝图实际 可以理解为一个存储一组视图方法的容器对象,特点:
- 一个应用可以有多个蓝图
- 可以将一个Blueprint注册到任何一个未使用的URL下 如:‘/use’
- Blueprint可以单独具有自己的模板,静态文件或者其他通用操作方法,它并不是必须要实现应用的视图和函数
- 一个应用初始化时,就应该要注册需要使用的Blueprint
- 一个Blueprint并不是一个完整的应用,不能独立于应用运行,而必须要注册到一个应用中
- 应用蓝图
- 创建一个蓝图对象
- user_bp=Blueprint(‘user’,name)
- 在蓝图上进行操作,注册路由,指定静态文件夹,注册模板过滤器
"""
#1.创建蓝图对象
user_bp=Blueprint('user',__name__)
#2在蓝图上操作,注册路由,指定静态文件夹,注册模板过滤器
@user_bp.route('/')
def user_profile():
return 'user_profile'
#3.在应用对象上注册这个蓝图
app.register_blueprint(user_bp)
"""
""""""
from goods import good_bp
app.register_blueprint(good_bp)
- 蓝图内部的静态文件
- 和应用对象不同,蓝图对象创建时不会默认注册静态目录的路由,需要在创建时指定static_folder 参数
admin=Blueprint('admin',__name__,static_folder='static_admin')
app.register_blueprint(admin,url_prefix='admin')
admin=Blueprint('admin',__name__,static_folder='static_admin',static_url_path='/lib')
app.register_blueprint(admin,url_prefix='/admin')
admin=Blueprint('admin',template_folder='my_templates')
- Flask Debug模式的作用
- 后端出现错误,会返回真实的错误信息给前端
- 修改代码后,自动重启开发服务器
- 支持options请求不等价于实现了CORS跨域解决方案
6.处理请求
- 需求,在视图编写中需要读取客户端携带的数据时,如何才能正确的取出数据?
- 请求携带的数据可能出现在HTTP报文中的不同位置,需要使用不同的方法来获取参数
- URL路径参数(动态路由)
- 例:有一个请求访问的接口地址为user/123,其中123实际上为具体的请求参数,表明请求123号用户的信息,此时如何从url中提取123的数据?
- Flask不同与Django直接定义路由时编写正则表达式的方式,而是采用转换器语法:
- 此处的《》既是一个转换器,默认为字符串类型,即将该位置的数据以字符串格式进行匹配,并以字符串为数据类型,name为参数名传入视图,若匹配不到会返回404,如int不能匹配111sss
@app.route('/user/<name>')
def user(name):
return 'hello %s'%name
- 转换器的使用
- DEFAULT_CONVERTERS={
- ’default’: UnicodeConverter,
- ’string’: UnicodeConverter,
- ’any’:AnyConverter,
- ’path’:PathConverter,
- ’int’:ItegerConverter,
- ’float’:FloatConverter,
- ’unid’:UNNIDCONcerter,
- }
- 自定义转换器
- 自定义转换器的类
- 将自定义转换器告知Flask应用
- 在使用转换器的地址定义使用
from werkzeug.routing import BaseConverter
class MobileConverter(BaseConverter):
'''手机号格式'''
regex=r'1[3-9]\d{9}'
app.url_map.converters['mobile']=MobileConverter
@app.route('/user/<mobile:mob_num>')
def sen_code(mob_num):
return 'send_code{}'.format(mob_num)
- 其他参数
- 若想获得其他地方传递的参数,可以通过Flask提供的request对象来读取。
- 不同位置的参数都放在request的不同属性中
- 属性–说明–类型
- data --记录请求的数据,并转换为字符串–*
- form–记录请求中的表单数据–MultilDct
- args–记录请求中的查询参数–MultiDict
- cookies–记录请求中的cookie信息–Dict
- headers–记录请求中的报文头–EnvironHeaders
- method–记录请求使用的HTTP方法–GET/POST
- url–记录请求的URL地址–string
- files–记录请求上传的文件–*
@app.route('/articles')
def get_articles():
channel_id=request.args.get('channel_id')
return 'get articles or channel {}'.format(channel_id)
- 上传图片
- 客户端上传图片到服务器,并保存到服务器中
@app.route('/upload',methods=['POST'])
def upload_file():
f=request.files('images')
f.save('./demo.png')
return 'ok'
7. 处理响应
- 需求—如何在不同的场景中返回响应信息?
- 返回模板
- 使用render_template 方法渲染模板并返回
@app.route('/index2')
def home():
data=dict(
my_str='fsfs',
my_int=432
)
return render_template('index2.html',**data)
- 重定向与json
- return json.dumps() 不会设置响应头
- return jsonify
- 转换成json格式字符串
- 设置响应头Content-Type:application/json
"""
@app.route('/demo2')
def demo2():
return redirect('http://www.baidu.com')
"""
@app.route('/demo3')
def demo3():
json_dict={
'user_id':10,
'user_name':'laowang'
}
return jsonify(json_dict)
- 自定义状态码与响应头
- 元组方式
- 可以返回一个元组,这样的元组必须是(response,status,headers)的形式,且至少包含一个元素,status值会覆盖状态码,headers可以是一个列表或字典,作为额外的消息标头值
- make_response
"""
@app.route('/demo4')
def demo4():
# return '状态码为666',666 #响应体 状态码 响应头 省略时只能从右边省略
# return '状态码 666',666,[('Itcast','Python')]
return '状态码为666',666,{'Itcast':'Python'}
"""
@app.route('/demo5')
def demo5():
resp=make_response('make_response测试')
resp.headers['Itcast']='Python'
resp.status='404 Not Found'
return resp
8.Cookie和Session
- Cookie
"""
@app.route('/cookie')
def s_cookie():
resp=make_response('set cookie ok')
resp.set_cookie('username','itcast',max_age=3600)# cookie名字 cookie的值 max_age(可选参数)设置有效期
return resp
"""
"""
@app.route('/getcookie')
def get_cookie():
resp=request.cookies.get('username')
return resp
"""
"""
@app.route('/deletecookie')
def delete_cookie():
resp=make_response('delete cookie')
resp.delete_cookie('username')
return resp
"""
- Session
- 需要先设置SECRET_KEY
- flask将session数据保存在哪?
- flask是浏览器session,将数据放在浏览器缓存
- SETRET_KEY相当于签名,保证session数据的安全性
class DefaultConfig(object):
SECRET_KEY='gfsgsg'
app.config.from_object(DefaultConfig)
@app.route('/setsession')
def set_session():
session['username']='itcast'
return 'set session OK'
@app.route('/getsession')
def get_session():
username=session.get('username')
return 'get session username{}'.format(username)
9.异常处理
- HTTP异常主动抛出
- abort 方法(终止请求返回异常)
- 抛出一个给定状态码的HTTPException 或者 指定响应,如想要一个页面找到异常来终止请求,可以使用abort(404)
- 参数 code-HTTP的错误状态码
- abort(404) / abort(500)
- 抛出状态码时,只能抛出HTTP协议的错误状态码
- 捕获错误
- errorhandler装饰器
- 注册一个错误处理程序,当程序抛出指定错误状态码时,就会调用该装饰器所装饰的方法
- 参数:
- code_or_exception-HTTP的错误状态码或指定异常
- 例如:统一处理状态码为500的错误给用户友好的请求
10.请求钩子
启动中间件/中间层的作用 请求处理过程: pre_process—>view----->after process request 请求处理流程: middleware1.pre_process()–>m2.pre_process()–>m3.pre_process()-->view()–>m3.after_process()–>m2.after_process()–>m1.after_process() 中间件不区分具体哪个视图,对所有视图都生效
- 在客户端和服务端交互的过程中,有些准备工作或扫尾工作需要处理,如:
- 在 请求开始时,建立数据库连接
- 在请求开始时,根据需求进行权限校验
- 在请求结束时,指定数据的交互格式
- 为了让每个视图函数避免编写重复功能的代码,Flask提供了通用的设施功能,即请求钩子,请求钩子通过装饰器的形式实现,flask支持如下四种请求钩子
- before_first_request 处理第一个请求前执行
- before_request 在每次请求前执行(若在某装饰器的函数返回了一个响应,视图函数不再被调用)
- after_request
- 若没有抛出异常,在每次请求后执行
- 接收一个参数:视图函数作出的响应
- 在此函数中可以对响应值在返回之前做最后一步修改处理
- 需要将参数中的响应在此参数中进行响应
- teardown_request:
- 在每次请求后执行
- 接受一个参数:错误信息,如果有相关错误抛出
@app.before_first_request
def before_first_request():
print('pre_first_request')
@app.before_request
def before_request():
print('before_request')
@app.after_request
def after_request(response):
print('after_request')
response.headers['Content-Type']='application/json'
return response
@app.teardown_request
def teardown_request(response):
print('teardown_request {}'.format(response))
@app.route('/')
def index1():
print('view called')
return 'index'
11.上下文
上下文,即语境、语意,在程序中可以理解为在代码执行到某一时刻时,根据之前代码所做的操作及下文即将要执行的逻辑,可以决定在当前时刻下可以使用到的变量,或者可以完成的事情 flask中有两种上下文,请求上下文和应用上下文 Flask中上下文对象:相当于一个容器,保存了Flask程序运行过程中的一些信息
- 请求上下文(request context)
- 思考:在视图函数中,如何取到当前请求的相关数据?如:请求地址,cookie
- 在flask中,可以直接在视图函数中使用request这个对象进行获取相关数据,而request就是请求上下文的对象,保存了当前当次请求的相关数据,请求上下文对象有:request,session
- request 封装了HTTP请求的内容,针对的是HTTP请求,举例:user=request.args.get('user) 获取的是get请求参数
- session 用来记录请求会话中的信息,针对的是用户信息,举例:session[‘name’]=userid 可以记录用户信息,还可以通过session.get(‘name’)获取用户信息
- 应用上下文(application context)
- 不是一直都存在的,它只是request context中的一个对app的代理,所谓local proxy,它的作用主要是帮助request获取当前的应用,它是伴request而生,随着request而灭
- 应用上下文对象有:current_app, g
- current_app(app=Flask(__ name__))
- 应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name() 打印当前app的名称,也可用current_app中存储一些变量,如:
- 应用启动的脚本是哪个文件,启动时指定了哪些参数
- 加载了哪些配置文件,导入了哪些配置
- 链了哪个数据库
- 有哪些public的工具类,常量
- 应用跑在哪个机器上,ip多少,内存多大
- 可以在子文件中应用主文件的app对象
- g对象
- g作为flask程序的全局变量的一个局部变量,充当中间媒介的作用,我们可以通过他在一次请求调用的多个函数间传递一些数据,只与当前请求有关的 , 每次请求都会重设这个变量
from flask import g
def db_query():
user_id=g.user_id
user_name=g.user_name
print('user_id={},user_name={}'.format(user_id,user_name))
@app.route('/')
def get_user():
g.user_id=4213
g.user_name='fds'
ret=db_query()
return 'g对象应用'
12.上下文综合应用
- 需求
- 构建认证机制
- 对于特定视图可以提供强制要求用户登录的限制
- 对于所有视图,无论是否强制要求用户登录,都可以在视图中尝试获取用户认证后的身份信息
from flask import request,abort,current_app,g
@app.before_request
def auther():
'''利用before_request请求钩子,在进入所有视图前先尝试判断用户的身份: return '''
g.user_id=234
def login_require(fun):
def wrapper(*args,**kwargs):
if g.user_id==None:
abort(401)
else:
return fun(*args,**kwargs)
return wrapper
@app.route('/')
def index():
return 'get user_id is {}'.format(g.user_id)
@app.route('/get')
@login_require
def get_user():
return 'get user_id is {}'.format(g.user_id)
13.独立使用flask上下文方式
- app_context与request_context
- 在flask程序运行的情况下,调试代码时需要使用current_app,g,request这些对象,该如何使用?
- app_context
- app_context 为我们提供了应用上下文环境,允许我们在外部使用应用上下文current_app,g可以通过with语句进行使用
- with app.app_context():
- request_context
- request_context 提供了请求上下文环境,允许我们在外部使用请求上下文request, session ,可以通过with语句进行使用
from flask import Flask
app=Flask(__name__)
app.redis_cli='redis_cli'
from flask import current_app
with app.app_context():
print(current_app.redis_cli)
from flask import Flask
app=Flask(__name__)
environ={'wsgi.version':(1,0),'wsgi_input':''}
with app.request_context(environ):
print(request.path)
14.上下文实现原理
- Threadlocal 线程局部变量
- 可以理解为将线程id与数据组合为字典保存
15.定制返回JSON格式
- 需求 想要接口返回的JSON数据具有如下统一格式:
- {‘message’:‘描述’,‘data’:{要返回的具体数据}}
- 在接口处理的正常情况下,message返回ok即可,但是若想每个接口正确返回省略message字段
class DemoResource(Resource):
def get(self):
return ('user_id':1,'name':'itcase')
{'messgae':'ok','data':{'user_id':1,'name':'itcast'}}
api=Api(app)
@api.representation('application/json')
def handle_json(data,code,headers):
return resp
你为天鹅我为蟾,千思百梦饱一餐
|