1. Flask简介:
flask
是一款非常流行的Python Web
框架,出生于2010年,作者是Armin Ronacher
,本来这个项目只是作者在愚人节的一个玩笑,后来由于非常受欢迎,进而成为一个正式的项目。目前为止最新的版本是0.12.2
。
flask
自2010年发布第一个版本以来,大受欢迎,深得开发者的喜爱,并且在多个公司已经得到了应用,flask能如此流行的原因,可以分为以下几点:
- 微框架、简洁、只做他需要做的,给开发者提供了很大的扩展性。
-
Flask
和相关的依赖(Jinja2
、Werkzeug
)设计得非常优秀,用起来很爽。 - 开发效率非常高,比如使用
SQLAlchemy
的ORM
操作数据库可以节省开发者大量书写sql
的时间。 - 社会活跃度非常高。
Flask
的灵活度非常之高,他不会帮你做太多的决策,即使做已经帮你做出选择,你也能非常容易的更换成你需要的,比如:
- 使用
Flask
开发数据库的时候,具体是使用SQLAlchemy
还是MongoEngine
或者是不用ORM
而直接基于MySQL-Python
这样的底层驱动进行开发都是可以的,选择权完全掌握在你自己的手中。区别于Django
,Django
内置了非常完善和丰富的功能,并且如果你想替换成你自己想要的,要么不支持,要么非常麻烦。 - 把默认的
Jinija2
模板引擎替换成Mako
引擎或者是其他模板引擎都是非常容易的。
flask文档
中文文档: http://docs.jinkan.org/docs/flask/
英文文档: http://flask.pocoo.org/docs/0.12/
2. 第一个flask程序:
用pycharm
新建一个flask
项目,新建项目的截图如下:
点击
create
后创建一个新项目,然后在helloworld.py
文件中书写代码:
#coding: utf8
# 从flask框架中导入Flask类
from flask import Flask
# 传入__name__初始化一个Flask实例
app = Flask(__name__)
# app.route装饰器映射URL和执行的函数。这个设置将根URL映射到了hello_world函数上
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
# 运行本项目,host=0.0.0.0可以让其他电脑也能访问到该网站,port指定访问的端口。默认的host是127.0.0.1,port为5000
app.run(host='0.0.0.0',port=9000)
然后点击运行,在浏览器中输入http://127.0.0.1:9000就能看到hello world
了。需要说明一点的是,app.run
这种方式只适合于开发,如果在生产环境中,应该使用Gunicorn
或者uWSGI
来启动。如果是在终端运行的,可以按ctrl+c
来让服务停止。
3. Flask创建app对象
3.1初始化参数
app = Flask()
的可选参数:
import_name
: 导入路径(寻找静态目录与模板目录位置的参数),一般写__name__
。
static_url_path
:访问静态资源的前缀,默认static
,这个前缀目录下都是静态资源。
static_folder
: 静态目录的名字,默认static
,其实就是项目中项目目录存放静态资源文件夹的名字。
template_folder
: 模板目录的名字,默认templates
, 其实就是项目中项目目录存放模板文件文件夹的名字。
3.2 配置参数
有4种,一般使用app.config.from_pyfile
和app.config.from_object
,app.config[key]=value
,app.config.from_envvar
是从虚拟环境中读取配置参数(一般不用):
- 使用配置文件,项目里新建一个文件,把相关的配置写到该文件中然后通过
app
对象的config
属性的from_pyfile
方式来导入配置文件。如在项目目录中定义配置文件为“config.cfg”
,在程序中引入配置文件方式:app.config.from_pyfile(“config.cfg”)
,路径可以是相对路径或者绝对路径,这种方式,可以传递silent=True
,那么这个静态文件没有找到的时候,不会抛出异常。
app.config.from_pyfile('config.cfg',silent=True)
# silent=True表示如果配置文件不存在的时候不抛出异常,默认是为False,会抛出异常。
- 从对象中导入,一般是定义一个配置参数的类(类名可以自定义,一般类名是
Config
),再把相关的参数配置以类属性的方式定义,然后通过app
对象的config
属性的from_ object
方式来导入。如定义一个Config
类:
class Config(object):
DEBUG = True
导入方式:app.config.from_object(Config)
,可以不用实例化一个Config
对象,因为类也是对象,flask
会自动从Config
类中读取参数
- 可以通过
app.config[key]=value
,类似于字典的方式直接把相关的配置参数加上。如:
app = Flask(__name__)
app.config['DEBUG'] = True
3.3 在视图读取配置参数
app.config.get()
按照字典取值的方法获取配置参数的值或者是先导入current_app
再用current_app.config.get()
按照字典取值的方法获取配置参数的值
3.4 app.run的参数
启动app可以传参数,比如host
、port
,debug
,如果是配置参数,配置参数只能传debug
app.run(host="0.0.0.0", port=5000,debug=True)
4. debug模式
4.1 为什么需要开启DEBUG模式
- 如果开启了
DEBUG
模式,那么在代码中如果抛出了异常,在浏览器的页面中可以看到具体的错误信息,以及具体的错误代码位置。方便开发者调试。 - 如果开启了
DEBUG
模式,那么以后在Python
代码中修改了任何代码,只要按ctrl+s
,flask
就会自动的重新记载整个网站。不需要手动点击重新运行。
4.2 debug配置的5种方式
使用配置文件,项目里新建一个文件,把相关的配置写到该文件中然后通过
app
对象的config
属性的from_pyfile
方式来导入配置文件。如在项目目录中定义配置文件为“config.cfg”
,在程序中引入配置文件方式:app.config.from_pyfile(“config.cfg”)
,路径可以是相对路径或者绝对路径,这种方式,可以传递silent=True
,那么这个静态文件没有找到的时候,不会抛出异常。从对象中导入,一般是定义一个配置参数的类(类名可以自定义,一般类名是
Config
),再把相关的参数配置以类属性的方式定义,然后通过app
对象的config
属性的from_ object
方式来导入。如定义一个Config
类:
class Config(object):
DEBUG = True
导入方式:app.config.from_object(Config)
,可以不用实例化一个Config
对象,因为类也是对象,flask
会自动从Config
类中读取参数。
可以通过
app.config[key]=value
,类似于字典的方式直接把相关的配置参数加上。如app.config["DEBUG"]=True
直接通过
app.debug = True
的方式通过给
app.config
添加参数的方式(类似于添加字典),app.config.update(DEBUG = True)
4.3 PIN码
开启了debug
模式后,flask会自动生成一个PIN
码,PIN
码用于页面调试如果想要在网页上调试代码,那么应该输入PIN
码。
5. url详解
5.1 在局域网中让其他电脑访问我的网站
如果想在同一个局域网下的其他电脑访问自己电脑上的Flask
网站,那么可以在app.run()
中设置host='0.0.0.0'
才能访问得到。
5.2 指定端口号
Flask
项目,默认使用5000
端口。如果想更换端口,那么可以在app.run()
中设置port=9000
5.3 url唯一
在定义url
的时候,一定要记得在最后加一个斜杠。
- 如果不加斜杠,那么在浏览器中访问这个url的时候,如果最后加了斜杠,那么就访问不到。这样用户体验不太好。
- 搜索引擎会将不加斜杠的和加斜杠的视为两个不同的
url
。而其实加和不加斜杠的都是同一个url
,那么就会给搜索引擎造成一个误解。加了斜杠,就不会出现没有斜杠的情况。
5.4 GET请求和POST请求
在网络请求中有许多请求方式,比如:GET、POST、DELETE、PUT
请求等。那么最常用的就是GET
和POST
请求了。
GET
请求:只会在服务器上获取资源,不会更改服务器的状态。这种请求方式推荐使用GET
请求。POST
请求:会给服务器提交一些数据或者文件。一般POST
请求是会对服务器的状态产生影响,那么这种请求推荐使用POST
请求。-
关于参数传递:
-
GET
请求:把参数放到url
中,通过?xx=xxx
的形式传递的。因为会把参数放到url中,所以如果视力好,一眼就能看到你传递给服务器的参数。这样不太安全。 -
POST
请求:把参数放到Form Data
中。会把参数放到Form Data
中,避免了被偷瞄的风险,但是如果别人想要偷看你的密码,那么其实可以通过抓包的形式。因为POST
请求可以提交一些数据给服务器,比如可以发送文件,那么这就增加了很大的风险。所以POST
请求,对于那些有经验的黑客来讲,其实是更不安全的。
-
在
Flask
中,route
方法,默认将只能使用GET
的方式请求这个url
,如果想要设置自己的请求方式,那么应该传递一个methods
参数来限制访问方式。
@app.route('/sample', methods=['GET', 'POST'])
def my_list(page):
return 'my list'
5.5 url与函数的映射
从之前的helloworld.py
文件中,我们已经看到,一个URL要与执行函数进行映射,使用的是@app.route
装饰器。@app.route
装饰器中,可以指定URL的规则来进行更加详细的映射,比如现在要映射一个文章详情的URL,文章详情的URL是/article/id/,id有可能为1、2、3...,那么可以通过以下方式:
@app.route('/article/<id>/')
def article(id):
return '%s article detail' % id
其中<id>,尖括号是固定写法,语法为<variable_name>
,variable_name
默认的数据类型是字符串。如果需要指定类型,则要写成<converter:variable_name>
,其中converter
就是类型名称,可以有以下几种:
string
: 默认的数据类型,接受没有任何斜杠“/”的文本。
int
: 接受整形。
float
: 接受浮点类型。
path
: 和string的类似,但是接受斜杠。
uuid
: 只接受uuid字符串。
any
:可以指定多种路径,这个通过一个例子来进行说明:
@app.route('/<any(article,blog):url_path>/')
def item(url_path):
return url_path
以上例子中,item
这个函数可以接受两个URL,一个是/article/,另一个是/blog/。并且,一定要传url_path
参数,当然这个url_path
的名称可以随便。
如果不想定制子路径来传递参数,也可以通过传统的?=的形式来传递参数,例如:/article?id=xxx,这种情况下,可以通过request.args.get('id')
来获取id
的值。如果是post
方法,则可以通过request.form.get('id')
来进行获取。
6. url_for详解
6.1 url_for的基本使用
url_for
第一个参数,应该是视图函数的名字的字符串。后面的参数就是传递给url
。如果传递的参数之前在url
中已经定义了,那么这个参数就会被当成path
的形式给url
。如果这个参数之前没有在url
中定义,那么将变成查询字符串的形式放到url
中。
@app.route('/post/list/<page>/')
def my_list(page):
return 'my list'
print(url_for('my_list',page=1,count=2))
# 构建出来的url:/my_list/1/?count=2
6.2 为什么需要url_for
- 将来如果修改了
URL
,但没有修改该URL对应的函数名,就不用到处去替换URL了。 -
url_for
会自动的处理那些特殊的字符,不需要手动去处理。 - 强烈建议以后在使用url的时候,使用
url_for
来反转url。
url = url_for('login',next='/')
# 会自动的将/编码,不需要手动去处理。
# url=/login/?next=%2F
7. 动态路由
7.1 传递参数
传递参数的语法是:/<参数名>/
。然后在视图函数中,也要定义同名的参数。
7.2 参数的数据类型
- 如果没有指定具体的数据类型,那么默认就是使用
string
数据类型。 -
int
数据类型只能传递int
类型。 -
float
数据类型只能传递float
类型。 -
path
数据类型和string
有点类似,都是可以接收任意的字符串,但是path
可以接收路径,也就是说可以包含斜杠。 -
uuid
数据类型只能接收符合uuid
的字符串。uuid
是一个全宇宙都唯一的字符串,一般可以用来作为表的主键。 -
any
数据类型可以在一个url
中指定多个路径。例如:@app.route('/<any(blog,article):url_path>/<id>/') def detail(url_path,id): if url_path == 'blog': return '博客详情:%s' % id else: return '博客详情:%s' % id # 路由传递的参数默认当做string处理,这里指定int,尖括号中冒号后面的内容是动态的 @app.route('/user/<int:id>') def hello_itcast(id): return 'hello word %d' %id
7.3 接收用户传递的参数
- 第一种:使用path的形式(将参数嵌入到路径中),就是上面讲的。
- 第二种:使用查询字符串的方式,就是通过
?key=value
的形式传递的。@app.route('/d/') def d(): wd = request.args.get('wd') return '您通过查询字符串的方式传递的参数是:%s' % wd
- 如果你的这个页面的想要做
SEO
优化,就是被搜索引擎搜索到,那么推荐使用第一种形式(path的形式)。如果不在乎搜索引擎优化,那么就可以使用第二种(查询字符串的形式)。
7.4 app.url_map`查看所有路由
通过app.url_map
可以查看整个flask
中的路由信息
7.5 同一路由装饰多个视图函数
如果两个路由的请求方式相同,那么先定义的视图函数将会覆后定义的视图函数,如果不同则根据请求方式来调用视图
@app.route('/index')
def index():
return "hello"
@app.route('/index')
def index1():
return "hello1"
上面这种情况,在浏览器访问index,只会执行第一个视图函数,第二个视图函数将会被覆盖
@app.route('/index', methods=['POST'])
def index():
return "hello"
@app.route('/index', methods=['GET'])
def index1():
return "hello1"
上面这种方式,会根据请求方式来决定执行那个视图
7.6 同一视图多个路由装饰器
装饰器层叠就可以
@app.route('/index')
@app.route('/index1')
def index():
return "hello"
8. 自定义URL转换器
8.1 自定义URL转换器的方式
- 转换器是一个类,且必须继承自
werkzeug.routing.BaseConverter
。 - 在转换器类中,实现
to_python(self,value)
方法,这个方法的返回值,将会传递到view
函数中作为参数。 - 在转换器类中,实现
to_url(self,values)
方法,这个方法的返回值,将会在调用url_for
函数的时候生成符合要求的URL形式。 - 在自定义的类中,重写
regex
,也就是这个变量的正则表达式。 - 将自定义的类,映射到
app.url_map.converters
上。比如:app.url_map.converters['tel'] = TelephoneConverter
8.2 to_python的作用
这个方法的返回值,将会传递到view
函数中作为参数。
8.3 to_url
的作用
这个方法的返回值,将会在调用url_for
函数的时候生成符合要求的url
形式。
8.4 万能转换器
可以定义一个正则的万能表达式,可以根据传入的正则表达式提取相关参数:
from flask import Flask
from werkzeug.routing import BaseConverter
class Regex_url(BaseConverter):
def __init__(self,url_map,*args):
super(Regex_url,self).__init__(url_map)
self.regex = args[0]
app = Flask(__name__)
app.url_map.converters['re'] = Regex_url
@app.route('/user/<re("[a-z]{3}"):id>')
def hello_itcast(id):
return 'hello %s' %id
9. redirect重定向
9.1 重定向分类
重定向分为永久性重定向和暂时性重定向,在页面上体现的操作就是浏览器会从一个页面自动跳转到另外一个页面。比如用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此我们应该给他重定向到登录页面。
永久性重定向:
http
的状态码是301
,多用于旧网址被废弃了要转到一个新的网址确保用户的访问,最经典的就是京东网站,你输入www.jingdong.com
的时候,会被重定向到www.jd.com
,因为jingdong.com
这个网址已经被废弃了,被改成jd.com
,所以这种情况下应该用永久重定向。暂时性重定向:
http
的状态码是302
,表示页面的暂时性跳转。比如访问一个需要权限的网址,如果当前用户没有登录,应该重定向到登录页面,这种情况下,应该用暂时性重定向。
9.2 flask中重定向
flask
中有一个函数叫做redirect
,可以重定向到指定的页面。示例代码如下:
# 先导入redirect
from flask import Flask,request,redirect,url_for
app = Flask(__name__)
@app.route('/login/')
def login():
return '这是登录页面'
@app.route('/profile/')
def profile():
if request.args.get('name'):
return '个人中心页面'
else:
# redirect 重定向
return redirect(url_for('login'))
10. response详解
10.1 视图函数中可以返回哪些值
- 可以返回字符串:返回的字符串其实底层将这个字符串包装成了一个
Response
对象。 - 可以返回元组:这样的元组必须是
(response, status, headers)
的形式,且至少包含一个元素。status
值会覆盖状态代码,headers
可以是一个列表或字典,作为额外的消息标头值。元组的形式是(响应体,状态码,头部信息),也不一定三个都要写,写两个也是可以的。返回的元组,其实在底层也是包装成了一个Response
对象。 - 可以返回
Response
及其子类。
10.2 实现一个自定义的Response对象
- 继承自
Response
类。 - 实现方法
force_type(cls,rv,environ=None)
。 - 指定
app.response_class
为你自定义的Response
对象。 - 如果视图函数返回的数据,不是字符串,也不是元组,也不是Response对象,那么就会将返回值传给
force_type
,然后再将force_type
的返回值返回给前端。
from flask import Flask,Response,jsonify,render_template
# flask = werkzeug+sqlalchemy+jinja2
import json
app = Flask(__name__)
# 将视图函数中返回的字典,转换成json对象,然后返回
# restful-api
class JSONResponse(Response):
@classmethod
def force_type(cls, response, environ=None):
"""
这个方法只有视图函数返回非字符、非元组、非Response对象
才会调用
response:视图函数的返回值
"""
if isinstance(response,dict):
# jsonify除了将字典转换成json对象,还将改对象包装成了一个Response对象
response = jsonify(response)
return super(JSONResponse, cls).force_type(response,environ)
app.response_class = JSONResponse
@app.route('/')
def hello_world():
# Response('Hello World!',status=200,mimetype='text/html')
return 'Hello World!'
@app.route('/list1/')
def list1():
resp = Response('list1')
resp.set_cookie('country','china')
return resp
@app.route('/list2/')
def list2():
return 'list2',200,{'X-NAME':'zhiliao'}
@app.route('/list3/')
def list3():
return {'username':'zhiliao','age':18}
if __name__ == '__main__':
app.run(debug=True,port=8000)
10.3 abort函数
-
abort()
: 立即停止视图函数的执行,并且把相对应的信息返回到前端中使用abort函数可以立即终止视图函数的执行 并可以返回给前端特定的信息abort()
函数可以传递两种方式的信息,(1) ①:一种是传递状态码信息,例如:
abort(404)
,前端就会报出已经默认好的404错误信息
②:自定义错误信息,(用装饰器)定义一个视图函数这个函数的返回值会是前端所看到的最终的结果,这个视图函数的装饰器来定义错误的类型@app.errorhandler(404) # 定义404的错误类型
自定义的错误类型,要和
abort()
里面定义的错误类型一致(2) 另外一种方式是传递响应体的信息,
abort(Response("内容"))
,Response()
,里面的信息会在前端上面显示出来代码:404错误...加上自定义的错误信息如果使用传递响应体信息的话就会只有响应体信息的错误异常
from flask import Flask, request, abort,Response
app = Flask(__name__)
@app.route("/login", methods=["GET"])
def login():
name = ""
pwd = ""
if name != "zhangsan" or pwd != "admin":
"""
使用abort函数可以立即终止视图函数的执行
并可以返回给前端特定的信息
1. 传递状态码信息,必须是标准的http状态码
可以接受状态码信息,返回给前端已经默认好的信息
"""
abort(404)
"""
2. 传递响应体的信息, Response()里面的内容会在前端上显示出来
resp = Response("login failed")
abort(resp)
"""
return "login success"
"""自定义异常处理"""
@app.errorhandler(404)
def handle_404_error(error): # 接受一个错误信息
"""自定义的处理404错误方法"""
'''这个函数的返回值会是前端所看到的最终的结果'''
return "出现了404错误,错误信息:%s"%error
if __name__ == '__main__':
app.run(debug=True)
10.4 自定义异常处理
自定义错误信息,(用装饰器)定义一个视图函数这个函数的返回值会是前端所看到的最终的结果,这个视图函数的装饰器来定义错误的类型
@app.errorhandler(404)
def error(e):
return '您请求的页面不存在了,请确认后再次访问!%s'%e
10.5 make_response()详解
make_response()
,相当于DJango
中的HttpResponse
。
1.返回内容
from flask import make_response
@blue.route('/makeresponse/')
def make_response_function():
response = make_response('<h2>羞羞哒</h2>')
return response, 404
2.返回页面
from flask import make_response
@blue.route('/makeresponse/')
def make_response_function():
temp = render_template('hello.html')
response = make_response(temp)
return response
- 注意:
make_response
想要返回页面,不能直接写做:make_response('hello.html')
,必须用render_template('hello.html')
形式。
3.返回状态码
- 方式一:在
make_response()
中传入状态码
from flask import make_response
@blue.route('/makeresponse/')
def make_response_function():
temp = render_template('hello.html')
response = make_response(temp, 200)
return response
- 方式二:直接
return
状态码
from flask import make_response
@blue.route('/makeresponse/')
def make_response_function():
temp = render_template('hello.html')
response = make_response(temp)
return response, 200
11. 获取请求参数
11.1 request详解
获取请求参数时首先要导入request
模块
from flask import request
就是 Flask
中表示当前请求的request
对象,request
对象中保存了一次HTTP
请求的一切信息。
11.2 request的常用属性
-
methods
默认的请求方式只有GET
,其他请求都需要通过参数methods
进行指定。
methods=['GET', 'POST']
-
args
:获取GET请求参数
flask中,要获取get
请求的参数,不是通过request.GET.get()
,而是通过request.args.get()
获取。
request.agrs.get('参数名')
-
form
:获取POST请求参数
flask中,要获取get
请求的参数,不是通过request.POST.get()
,而是通过request.form.get()
获取。
request.form.get('参数名')
11.3 上传文件
已上传的文件存储在内存或是文件系统中一个临时的位置。你可以通过请求对象的files
属性访问它们。每个上传的文件都会存储在这个字典里。它表现近乎为一个标准的 Python file
对象,但它还有一个 save()
方法,这个方法允许你把文件保存到服务器的文件系统上。这里是一个用它保存文件的例子:
from flask import request
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/uploaded_file.txt')
...
如果你想知道上传前文件在客户端的文件名是什么,你可以访问filename
属性。但请记住, 永远不要信任这个值,这个值是可以伪造的。如果你要把文件按客户端提供的文件名存储在服务器上,那么请把它传递给Werkzeug
提供的secure_filename()
函数:
from flask import request
from werkzeug import secure_filename
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['the_file']
f.save('/var/www/uploads/' + secure_filename(f.filename))