一. 初识Flask
Flask诞生于2010年,是Armin ronacher(人名)用 Python 语言基于 Werkzeug 工具箱编写的轻量级Web开发框架。 Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login,数据库Flask-SQLAlchemy),
都需要用第三方的扩展来实现。比如可以用 Flask 扩展加入ORM、窗体验证工具,文件上传、身份验证等。Flask 没有默认使用的数据库,你可以选择 MySQL,也可以用 NoSQL。 其 WSGI 工具箱采用 Werkzeug(路由模块),模板引擎则使用 Jinja2。这两个也是 Flask 框架的核心。 官网: http://flask.pocoo.org/ 官方文档: <http://docs.jinkan.org/docs/flask/>
二.常用扩展包
- Flask-SQLalchemy:操作数据库,ORM; - Flask-script:插入脚本; - Flask-migrate:管理迁移数据库; - Flask-Session:Session存储方式指定; - Flask-WTF:表单; - Flask-Mail:邮件; - Flask-Bable:提供国际化和本地化支持,翻译; - Flask-Login:认证用户状态; - Flask-OpenID:认证; - Flask-RESTful:开发REST API的工具; - Flask JSON-RPC: 开发rpc远程服务[过程]调用 - Flask-Bootstrap:集成前端Twitter Bootstrap框架 - Flask-Moment:本地化日期和时间 - Flask-Admin:简单而可扩展的管理接口的框架 可以通过 http://flask.pocoo.org/extensions/ 查看更多flask官方推荐的扩展
三.安装
1.开发都在虚拟环境中,所以先创建一个我们需要使用虚拟环境
mkvirtualenv flask——demo -p python3 #创建一个指定python版本的虚拟环境
2.虚拟环境中安装flask
pip install flask
四.使用
基本使用
与django不同,flask不会提供任何的自动操作,所以需要手动创建项目目录,需要手动创建启动项目的管理文件
例如,
1.创建项目目录 flaskdemo,
2.在目录中创建manage.py.
3.在pycharm中打开项目并指定上面创建的虚拟环境
from flask import Flask app = Flask(__name__,
static_url_path="/python", #访问静态资源的url前缀,默认是static,这样写后,你要访问static文件夹的资源index.html就需要访问127../python/index.html
static_folder="static", #静态文件目录,默认就是static
template_folder="templates" #模板文件目录,默认就是templates
) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run()
解析:
首先,我们导入了 Flask 类。这个类的实例将会是我们的 WSGI 应用程序。
接下来,我们创建一个该类的实例,第一个参数是应用模块或者包的名称。 如果你使用单一的模块(如本例),
你应该使用 __name__ ,因为模块的名称将会因其作为单独应用启动还是作为模块导入而有不同( 也即是 '__main__' 或实际的导入名)。
这是必须的,这样 Flask 才知道到哪去找模板、静态文件等等。详情见 Flask 的文档。
然后,我们使用 route() 装饰器告诉 Flask 什么样的URL 能触发我们的函数。
这个函数的名字也在生成 URL 时被特定的函数采用,这个函数返回我们想要显示在用户浏览器中的信息。
最后我们用 run() 函数来让应用运行在本地服务器上。
其中 if __name__ == '__main__': 确保服务器只会在该脚本被 Python 解释器直接执行的时候才会运行,而不是作为模块导入的时候。
调试模式
虽然 run() 方法适用于启动本地的开发服务器,但是你每次修改代码后都要手动重启它。这样并不够优雅,而且 Flask 可以做到更好。
如果你启用了调试支持,服务器会在代码修改后自动重新载入,并在发生错误时提供一个相当有用的调试器。
有两种途径来启用调试模式。一种是直接在应用对象上设置:
#法一 app.debug = True app.run() #法二 app.run(debug=True) #其实还有第三种 #先设置一个配置类 # 配置类 class Config(object): app.debug = True #app再应用配置类(如果这个类和app不在同一个文件的话需要去导入) app.config.from_object(Config)
#第四种
app.config.from_pyfile("config.cfg")
#第五种
app["DEBUG"]=TRUE
读取配置参数
@app.route('/') def index(): dict1={ 'name':'haha', 'age':18, 'sex':'2' } #读取配置参数,方法一 print(app.config["DEBUG"]) print(app.config.get("DEBUG")) #读取配置参数,方法二,通过current_app current_app.config.get("DEBUG") list1=['张三','李四','王五'] g.name='wahahhahah' return render_template('index.html',title='我的flask网页',dict1=dict1,list1=list1,func=func)
run参数
app.run() #默认是127.0.0.1::5000 #通过192.168....是访问不了的 #如果想要两个都可以使用,就必须绑定0.0.0.0这个ip run(host="0.0.0.0",port=5000)
路由
route() 装饰器把一个函数绑定到对应的 URL 上,访问url会触发函数
查看整个app的所有路由
print(app.url_map)
基本形式:
@app.route('/') def hello(): return 'hello dajiba'
带参数的路由:
#传递参数的两种方式 #方式一,没有限定类型 @app.route('/user/<user_id>') def user_info(user_id): return 'hello %s'%user_id #方式二,限定参数类型 @app.route('/user/<int:user_id>') def user_info2(user_id): return 'hello %d'%user_id
限定请求方法的路由:
# 路由限定请求方式 @app.route('/get', methods=['get', 'post']) def demo(): # 直接返回请求方法 # 注意,request记得导包 return request.method
同一路由装饰不同的视图函数
@app.route('/') def h1(): return 'h1' @app.route('/') def h2(): return 'h2' #上面这样默认走视图函数一,视图函数一排在比较前面 @app.route('/',method=['get']) def h1(): return 'h1' @app.route('/',method=['post']) def h2(): return 'h2' #这种方式会根据骑牛方法来决定走具体的哪一个函数
同一视图函数使用不同的路由
@app.route('/hi1') @app.route('/hi2') def h1(): return 'h'
自定义转换器:
# 1.导入转化器基类 # 2. 自定义转换器 # 3. 添加转换器到默认转换器字典中。并指定转换器的名字 # 4.使用自定义转换器
from werkzeug.routing import BaseConverter
class RegexConverter(BaseConverter): def __init__(self, url_map, *args): super(RegexConverter, self).__init__(url_map) # 将接受的第一合格参数作为匹配规则保存 self.regex = args[0] app.url_map.converters['re'] = RegexConverter @app.route('/user/<re("[0-9]{3}"):user_id>') def user_info(user_id): return 'hello %s' % user_id # 一个路由中可以接受多个路由参数,并且路由转换器也可以使用多个 @app.route("/user/<re('d{3}'):user_id>/avatar/<re('w{4,8}'):avatar_id>",methods=["get"]) def user2(user_id,avatar_id): print(request.method) return "用户%s,头像id:%s" % (user_id,avatar_id)
系统自带转换器类型:
DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'path': PathConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
url_for路由重定向
@app.route('/') def index(): dict1={ 'name':'haha', 'age':18, 'sex':'2' } #读取配置参数,方法一 print(app.config["DEBUG"]) print(app.config.get("DEBUG")) #读取配置参数,方法二,通过current_app current_app.config.get("DEBUG") list1=['张三','李四','王五'] g.name='wahahhahah' return render_template('index.html',title='我的flask网页',dict1=dict1,list1=list1,func=func) @app.route('/') def h1(): #使用url_for函数,通过视图函数的名字找到视图函数对应的url路径 return redirect(url_for("index"))
请求(request)
request:flask中代表当前请求的 request 对象 作用:在视图函数中取出本次请求数据 导入:from flask import request 常用的属性如下:
说明 | 类型 | |
---|---|---|
data | 记录请求的数据,并转换为字符串 | * |
form | 记录请求中的表单数据 | MultiDict |
args | 记录请求中的查询参数 | MultiDict |
cookies | 记录请求中的cookie信息 | Dict |
headers | 记录请求中的请求头 | EnvironHeaders |
method | 记录请求使用的HTTP方法 | GET/POST |
url | 记录请求的URL地址 | string |
files | 记录请求上传的文件 | * |
#请求对象request #1. 获取请求对象中的串参数 #http://127.0.0.1:5000/?username=123aad&lv=1&lv=2&lv=3 @app.route('/') def index(): print('request_args',request.args) #request_args ImmutableMultiDict([('username', '123aad'), ('lv', '1'), ('lv', '2'), ('lv', '3')]) username=request.args.get('username') #获取单个请求字符串,通过get亲请求只能获取同名参数的第一个值
print(username) #123aad parmas=request.args.to_dict() #获取所有请求字符串并转换为字典 {'username': '123aad', 'lv': '1'} print(parmas) # ['1', '2', '3'] love=request.args.getlist('lv') #获取所有的请求参数的值,包括同名字段使用getlist print(love) return 'nidaye'
#关于data和form的差别
1.如果是传统的form表单, conten-type格式是urlencode的就从request.form对象中取值
2.如果是json格式的数据,application/json格式的数据,就去request.data获取参数
3.第一第二条是针对表单请求参数,如果是url请求参数就从request.args中获取数据
@app.route('/upload',methods=["POST"])
def upload():
"""接受前端传来的图片对象"""
file_obj=request.files.get('pic')
if file_obj is None:
return "未上传文件"
#传统的接受方法
#创建文件句柄
f=open("demo.png","wb")
#向文件写入内容demo.png
data=file_obj.read()
f.write(data)
# 关闭文件
f.close()
#使用flask接口
# file_obj.save("1111111.png")
return '上传成功'
响应(response)
flask默认支持2种响应方式: 数据响应: 默认响应html文本,也可以返回 JSON格式 页面响应: 重定向 url_for 响应的时候,flask也支持自定义http响应状态码 #响应 #1.响应html文本 @app.route('/img') def img(): #默认支持html文本 return '<img src="http://flask.pocoo.org/static/logo.png">' #2.响应json数据 #json数据就是字符串 @app.route('/js') def ds():
#方式一 # data=[{'id':1,'username':'user1'}, # {'id': 2, 'username': 'user2'}] dt={ 'id': 1, 'username': 'user1', 'id2': 2, 'username2': 'user2' } return jsonify(dt)
#方式二,传统
json_str=json.dumps(dt)
return json_str,200,{'Content-Type':'application/json'}
#这说明,jsonify帮助我们把字典转变成json数据,并把响应头设置Content-Type为application/json
#方式三
return jsonify('city'='shenzhen','country'='china')
#重定向 #重定向到百度 @app.route('/user') def user(): #页面跳转 redirect函数就是response对象的页面跳转的封装 # Location:http://www.baidu.com return redirect('http://www.baidu.com') #重定向到自己写的视图函数 #可以直接填自己的url #也可以使用url_for生成指定的视图函数所对应的url #使用url_for可以实现视图函数的方法之间的内部跳转 #url_for('视图方法名') @app.route('/login') def login(): return redirect(url_for('user')) #重定向到带有参数的视图函数 @app.route('/user/<user_id>') def user_info(user_id): return 'hello %s'%user_id # url_for("视图方法名",参数名1=参数值,参数名2=参数值,....) @app.route('/demo') def demo(): return redirect(url_for('user_info',user_id=1000)) #自定义状态吗 @app.route('/demo2') def demo2(): return '状态吗为666',400
@app.route('/response')
def response_demo():
#1.使用元组返回自定义响应信息
# 响应体 状态码 响应头
#即使我们没有用括号把这三个参数括起来,他也是一个元组(python会处理)
#响应头可以是列表套元组,也可以是字典的键值对形式
return 'index page',400,[("content-encoding","gzip"),("content-type","text/html; charset=utf-8")]
return 'index page',400,{"content-encoding","gzip","content-type","text/html; charset=utf-8"}
#状态码可以自定义,不必非要是标准的状态码
return 'index page',666,{"content-encoding","gzip","content-type","text/html; charset=utf-8"}
#第二个状态码参数可以添加状态码的描述信息,参数形式必须是字符串,第一个是状态码
return 'index page','666 status code ok',{"content-encoding","gzip","content-type","text/html; charset=utf-8"}
#可以省略请求头参,但是不能只省略状态码参数,而不省略请求头参数
return 'index page','666 status code ok'
#第二种方法
resp=make_response()
resp.status='999 hhahh' #设置状态码
resp.headers['city']='深圳'
return resp
会话控制
所谓的会话,就是用户和浏览器中网站之间一次交互过程. 会话的开始是在用户打开浏览器以后第一次访问网站. 会话的结束时在用户关闭浏览器以后. 因为 http 是一种无状态协议,浏览器请求服务器是无状态的。 无状态:指一次用户请求时,浏览器、服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求。 无状态原因:浏览器与服务器是使用 socket 套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的 socket 连接,而且服务器也会在处理页面完毕之后销毁页面对象。 有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等 实现状态保持主要有两种方式: - 在客户端存储信息使用`Cookie,本地存储` - 在服务器端存储信息使用`Session`,redis
cookie
Cookie是由服务器端生成,发送给客户端浏览器,浏览器会将Cookie的key/value保存,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。
Cookie的key/value可以由服务器端自己定义。 使用场景: 登录状态, 浏览历史, 网站足迹 Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用 Cookie基于域名安全,不同域名的Cookie是不能互相访问的 如访问luffy.com时向浏览器中写了Cookie信息,使用同一浏览器访问baidu.com时,无法访问到luffy.com写的Cookie信息 浏览器的同源策略针对cookie也有限制作用. 当浏览器请求某网站时,会将本网站下所有Cookie信息提交给服务器,所以在request中可以读取Cookie信息
cookie设置
#设置cookie需要通过flask的Response响应对象来进行设置,由flask内部 # 提供了一个make_response函数给我们可以快速创建响应对象 @app.route('/set_cookie') def set_cookie(): """set-Cookie: 变量名=变量值;""" # max_age=10 设置过期时间,单位为秒,没有设置有效期就是默认时间,就是关闭浏览器cookie失效 res=make_response('this is COOKIE') res.set_cookie('username','xiaoming',max_age=1000)
#这边要明白的就是set_cookie没有什么特殊的地方,就是在响应头中设置set_cookie传了一个字符串
#也可以这么干
res.headers['Set-Cookie']='age=5;name=zhangsan' return res #获取cookie @app.route('/get_cookie') def get_cookie(): # 返回就是字典 resp=request.cookies.get('username') return resp
#获取cookie
@app.route('/delete_cookie')
def delete_cookie():
resp=make_response('delete cookie')
#删除cookie,并不会直接删除浏览器的cookie,只是设置了过期时间,具体什么时候删除,浏览器会自动删除
resp.delete_cookie('xiaoming')
return resp
session
对于敏感、重要的信息,建议要存储在服务器端,不能存储在浏览器中,如用户名、余额、等级、验证码等信息 在服务器端进行状态保持的方案就是`Session` 注意: **Session依赖于Cookie**,而且flask中使用session,需要配置SECRET_KEY选项,否则报错.
#flask默认把session保存到cookie中(大多数框架,包括其他语言都是保存数据中)
#flask在保存到cookies中的时候,利用设置的SECRET_KEY对session进行了混淆之后写入了cookie
session需要导入 # 配置类 class Config(object): app.debug = True SECRET_KEY = 'wocoa' @app.route('/set_session') def set_session(): session['username']='lala' return 'ok' @app.route('/get_session') def get_session(): return session.get('username')