flask
flask是什么
官方文档:http://docs.jinkan.org/docs/flask/quickstart.html
Flask 是一个 Python 实现的 Web 开发微框架,
轻量级Web 开发框架
Flask 依赖两个外部库: Jinja2 模板引擎和 Werkzeug WSGI 工具集
Django和Flask有什么区别?
Flask
- Flask确实很“轻”,不愧是Micro Framework,从Django转向Flask的开发者一定会如此感慨,除非二者均为深入使用过
- Flask自由、灵活,可扩展性强,第三方库的选择面广,开发时可以结合自己最喜欢用的轮子,也能结合最流行最强大的Python库
- 入门简单,即便没有多少web开发经验,也能很快做出网站
- 非常适用于小型网站
- 非常适用于开发web服务的API
- 开发大型网站无压力,但代码架构需要自己设计,开发成本取决于开发者的能力
- 各方面性能均等于或优于Django
- Django自带的或第三方的好评如潮的功能,Flask上总会找到与之类似第三方
- Flask灵活开发,Python高手基本都会喜欢Flask,但对Django却可能褒贬不
- Flask与关系型数据库的配合使用不弱于Django,而其与NoSQL数据库的配合远远优于Django
- Flask比Django更加Pythonic,与Python的philosophy更加吻合
Django
- Django太重了,除了web框架,自带ORM和模板引擎,灵活和自由度不够高
- Django能开发小应用,但总会有“杀鸡焉用牛刀”的感觉
- Django的自带ORM非常优秀,综合评价略高于SQLAlchemy
- Django自带的模板引擎简单好用,但其强大程度和综合评价略低于Jinja2
- Django自带ORM也使Django与关系型数据库耦合度过高,如果想使用MongoDB等NoSQL数据,需要选取合适的第三方库,且总感觉Django+SQL才是天生一对的搭配,Django+NoSQL砍掉了Django的半壁江山
- Django目前支持Jinja等非官方模板引擎
- Django自带的数据库管理app好评如潮
- Django非常适合企业级网站的开发:快速、靠谱、稳定
- Django成熟、稳定、完善,但相比于Flask,Django的整体生态相对封闭
- Django是Python web框架的先驱,用户多,第三方库最丰富,最好的Python库,如果不能直接用到Django中,也一定能找到与之对应的移植
- Django上手也比较容易,开发文档详细、完善,相关资料丰富
主要的对比:
Flask提供简洁、灵活和细粒度控制。它很是客观(让您决定如何实现想要的东西)。
Django提供一种包罗万象的体验:您可以获得管理面板、数据库接口、ORM(object-relational mappling,对象关系映射),还有开箱即用的应用程序和项目的目录结构。
您应该选择:
Flask,如果您专注于体验和学习机会,或者您想对选用哪个组件有更大的控制权(例如您想使用哪个数据库和如何与它们交互)。
Django,如果你专注于最终的产品。特别是如果您正在做一个简单的应用程序(如一个新闻网站、电子商店,或者博客)并且希望有一个直接明了的做法。
换句话说,如果您是位初学者,Flask可能是个更好的选择,因为用到的组件比较少。
如果您想要更多的定制,那么Flask也是个更好的选择。
而且,根据我的数据工程师朋友的看法,
Flask更适合创建那些所谓的REST API的东西,因为它比Django更灵活。
另一方面,
如果您想构建一些简单的东西,
Django会让您更快地达到目标。
flask初步学习
创建虚拟环境
mkvirtualenv flask-py3 -p python3
安装
pip install Flask
案例
创建一个flask项目
from flask import Flask
Flask()
报错:
注意:在实例化(创建一个flask项目)的时候,他有一个必须的参数
from flask import Flask #导包
app = Flask(__name__) #创建以个flask应用,但必须指定程序所在的包__name__
@app.route('/') #实现了路径与视图的绑定,第一个参数是路径
def index():
return 'hello flask'
if __name__ == '__main__':
app.run() #启动web服务
这段代码有什么用
- 首先,我们导入了 Flask 类。这个类的实例将会是我们的 WSGI 应用程序。
- 接下来,我们创建一个该类的实例,第一个参数是应用模块或者包的名称。 如果你使用单一的模块(如本例),你应该使用 name,因为模块的名称将会因其作为单独应用启动还是作为模块导入而有不同( 也即是 ‘main’ 或实际的导入名)。这是必须的,Flask 才知道到哪去找模板、静态文件等等。详情见 Flask 的文档。
- 然后,我们使用 route() 装饰器告诉 Flask 什么样的URL 能触发我们的函数。
- 这个函数的名字也在生成 URL 时被特定的函数采用,这个函数返回我们想要显示在用户浏览器中的信息。
- 最后我们用 run() 函数来让应用运行在本地服务器上。 其中 if name == ‘main’:确保服务器只会在该脚本被 Python 解释器直接执行的时候才会运行,而不是作为模块导入的时候。
- Ctrl+C退出
注意点:现在的开发环境里面使用的是测试服务器,将来替换遵循WSGI协议的服务器产品
flask创建对象的几个参数
def __init__(
self,
import_name, #flask程序所在的包
static_url_path=None, #静态文件访问的路径
static_folder='static', #静态文件夹的名字
static_host=None,
host_matching=False,
subdomain_matching=False,
template_folder='templates',#模板文件夹
instance_path=None,
instance_relative_config=False,
root_path=None
):
def run(
self,
host=None, #路径
port=None,# 端口
debug=None,#调试
load_dotenv=True,
**options):
run的参数
:param host: the hostname to listen on. Set this to ```0.0.0.0```to
have the server available externally as well. Defaults to
```127.0.0.1```or the host in the ``SERVER_NAME`` config variable
if present.
:param port: the port of the webserver. Defaults to ``5000`` or the
port defined in the ``SERVER_NAME`` config variable if present.
:param debug: if given, enable or disable debug mode. See
:attr:`debug`.
:param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv`
files to set environment variables. Will also change the working
directory to the directory containing the first file found.
:param options: the options to be forwarded to the underlying Werkzeug
server. See :func:`werkzeug.serving.run_simple` for more
information.
app.url_map:查看所有路径对应内容
app.run(host=‘0.0.0.0’):这会让操作系统监听所有公网 IP。
关于调试模式的修改
调试模式状态下, 代码修改后会自动刷新
#第一种
app.debug = True
app.run()
#第二种
app.run(debug = True)
关于从类中加载
from flask import Flask
app = Flask(__name__) #创建flask应用,必须指定程序所在包的__name__
class Config(object): #配置加载对象
DEBUG = True
USERNAME = 'TOM'
app.config.from_object(Config) #将配置对象类加载进app
@app.route('/')
def index():
#获取配置的参数
print(app.config.get('DEBUG'))
print(app.config.get('USERNAME'))
return 'hello flask123'
if __name__ == '__main__':
app.run()
关于从文件中加载
自己在前文件夹书写的加载配置文件config.ini
DEBUG = True
USERNAME = 'Tony'
from flask import Flask
app = Flask(__name__)
#将配置文件加载进app
app.config.from_pyfile('config.ini')
@app.route('/')
def index():
#获取配置的参数
print(app.config.get('DEBUG'))
print(app.config.get('USERNAME'))
return 'hello flask3'
if __name__ == '__main__':
app.run()
获取配置的参数
上面写了完整代码
@app.route('/')
def index():
#获取配置的参数
print(app.config.get('DEBUG'))
print(app.config.get('USERNAME'))
return 'hello flask3'
关于路由的设置
关于源码的解读
大意:
关于路径的配置
注意/严格匹配,前/和后/都要注意
@app.route('/')
def index():
pass
# 带参数
@app.route('/<username>')
def show_user(username):
pass
# 指定格式的参数
@app.route('/post/<int:post_id>')
def show_post(post_id):
pass
第1个路由:捕获到 “/”,就进入函数进行处理,里面的函数叫做视图函数。URL从左到右第一个“/”为止一般 是首页,因此第1一个路由是处理首页。
第2个路由:捕获到 “/” ,其中尖括号<…> 这一部分是动态可变的部分,其中不可以带有斜杠的。而且这个动态部分当做参数传递给了视图函数。
第3个路由:捕获到 “/post/int:post_id” 其中 “/post/”是静态部分,“int:post_id”是动态部分,但是这个动态部分必须是个int类型的整数,同样其中不可以带有斜杠。同样这个动态部分也成为参数传入了视图函数show_post。这个rule是 converter:name的形式,而converter除了有int,还有float,path,string。
POST请求需要使用postman,GET请求可以用浏览器
from flask import Flask
#创建flask应用,但是必须指定程序所在包的__name__
app = Flask(__name__)
#关于路径配置
@app.route('/index/<int:user_id>',methods=['POST'])
def index(user_id):
print('获取用户的id',user_id)
print(app.url_map)
print(app.url_map)
return 'hello flask123'
if __name__ == '__main__':
#启动web服务
app.run(debug=True)
注意点:
1.Route 中,用于请求方式的配置,用methods这个方法进行指定,是一个列表
2.用<>来进行参数的指定
3.对于地址传参问题,可以用类型来约束,如果不写默认是string字符串类型
转换器类型:
string (缺省值) 接受任何不包含斜杠的文本
int 接受正整数
float 接受正浮点数
path 类似 string ,但可以包含斜杠
uuid 接受 UUID 字符串
还可以为同一个视图函数,定义多个URL规则:
这指定/users/将是第1页的URL,并且 /users/page/N将是第N页的URL
@app.route('/user/', defaults={'page':1})
@app.route('/user/page/<int:page>')
def show_user(page):
return 'user page : %s' % page
HTTP的请求方法
可以使用 route() 装饰器的 methods 参数来处理不同的 HTTP 方法:
from flask import request
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
return 'POST'
else:
return 'GET'
如果当前使用了 GET 方法, Flask 会自动添加 HEAD 方法支持,并且同时还会 按照 HTTP RFC 来处理 HEAD 请求。同样, OPTIONS 也会自动实现。
转换器
flask自带的转换器
#: the default converter mapping for the map.
DEFAULT_CONVERTERS = {
"default": UnicodeConverter,
"string": UnicodeConverter,
"any": AnyConverter,
"path": PathConverter,
"int": IntegerConverter,
"float": FloatConverter,
"uuid": UUIDConverter,
}
自定义转换器
定义
#进行转换器的重写
class MyCover(BaseConverter):
def __init__(self,parm1,*args):
print('接受传递过来的参数',parm1)
print(*args)#写自己的正则
#重写自带转换器
super().__init__(parm1)
self.regex = args[0]
注意点:
1.继承于baseconverter
2.需要额外的参数,用来承接将来需要书写的正则规则
3.要regex进行重新赋值
将自定义的转换器加入到默认的转换器字典中去
# 将自定义的转换器加入到默认的转换器字典中去
app.url_map.converters['re'] = MyCover
print(app.url_map.converters)
使用
#使用自定义的转换器
@app.route('/<re("show"):name>')
def showinfo(name):
return "这是自定义转换器的应用"
整合
from flask import Flask
from werkzeug.routing import BaseConverter,IntegerConverter
#创建flask应用,但是必须指定程序所在包的__name__
app = Flask(__name__)
#进行转换器的重写
class MyCover(BaseConverter):
def __init__(self,parm1,*args):
print('接受传递过来的参数',parm1)
print(*args)#写自己的正则
#重写自带转换器
super().__init__(parm1)
self.regex = args[0]
# 将自定义的转换器加入到默认的转换器字典中去
app.url_map.converters['re'] = MyCover
print(app.url_map.converters)
#使用自定义的转换器
@app.route('/<re("show"):name>')
def showinfo(name):
return "这是自定义转换器的应用"
if __name__ == '__main__':
#启动web服务
app.run(debug=True)
响应
视图函数的返回值会自动转换为一个响应对象。如果返回值是一个字符串,那么会被转换 为一个包含作为响应体的字符串、一个200 OK出错代码 和一个text/html类型的响应对象。以下是转换的规则:
- 如果视图返回的是一个响应对象,那么就直接返回它。
- 如果返回的是一个字符串,那么根据这个字符串和缺省参数生成一个用于返回的 响应对象。
- 如果返回的是一个元组,那么元组中的项目可以提供额外的信息。元组中必须至少 包含一个项目,且项目应当由(response, status, headers)或者(response, headers)组成。status的值会重载状态代码,headers是一个由额外头部值组成的列表或字典。
- 如果以上都不是,那么 Flask 会假定返回值是一个有效的 WSGI 应用并把它转换为 一个响应对象。
规定
展示
这样写会出现上面的报错
from flask import Flask
from flask import json,jsonify
#创建flask应用,但是必须指定程序所在包的__name__
app = Flask(__name__)
#关于路径的get请求方式配置
@app.route('/index/<int:user_id>',methods=['GET'])
def index(user_id):
print('获取用户的id',user_id)
print(app.url_map)
data = {
"name":"小明",
"age":18
}
return (data) #会报错,因为没有字典类型
#return jsonify(data)#要传递json数据才能的到data数据
if __name__ == '__main__':
#启动web服务
app.run(debug=True)
request请求里面数据
from flask import Flask,request
app = Flask(__name__)
@app.route('/index',methods=['GET','POST'])
def index():
print("=====================")
#通过request获取前端数据
print(request) #请求对象
print(request.url) #请求路径
print(request.method) #请求方法
print(request.args) #get请求路径中的参数
print('form',request.form)#post获取form表单里面的数据
# print(request.data)#post获取不到数据,但是可以获取bytes类型数据的图片
print('file',request.files)#post获取表单里面的文件
return 'index'
if __name__ == '__main__':
app.run(debug=True)
form与file的区别:
首先使用这两个方法的前提是post或者put请求
两者的区别在于处理不同mimetype类型的数据,返回值也不同。
当minitype为application/x-www-form-urlencoded或者multipart/form-data的时候,也就是我们
所谓表单提交,访问request.form会返回一个包含解析过的的表单对象的 MultiDict,而request.data是空的。
当flask遇到不能处理的mimetype时,请求的数据就不能被其它方式正常解析,这些方式包括request.form、request.json和request.files这几个常用的用来访问数据的属性。这时就把数据作为字符串存在request.data中。
这里注意一下request.json需要application/json的mimetype类型。
知道了这些处理数据的过程,那我们就可以对提交的数据进行扩展,定义一些自己专用的mimetype类型,并在Request类中定义处理专用mimetype数据的方法,从而让我们实现更个性、与众不同的功能需求。
重定向与报错
使用 redirect() 函数可以重定向。使用 abort() 可以 更早退出请求,并返回错误代码:
from flask import abort, redirect, url_for
@app.route('/')
def index():
return redirect(url_for('error'))
@app.route('/error')
def error():
abort(401)
this_is_never_executed()
上例实际上是没有意义的,它让一个用户从索引页重定向到一个无法访问的页面(401 表示禁止访问)。但是上例可以说明重定向和出错跳出是如何工作的。
缺省情况下每种出错代码都会对应显示一个黑白的出错页面。使用 errorhandler() 装饰器可以定制出错页面:
自定义出错内容
用装饰器,装饰器参数填404,401或者导包然后exception的属性选择对应的出错内容,比如这里用的是401对应Unathorized,400对应BadRequest:
from werkzeug import exceptions
# 自定义错误处理:用装饰器或者app注册
@app.errorhandler(exceptions.Unauthorized)
def handle_error(e):
return 'ooooooo bad', 401
用app注册来设置错误时返回的代码:
# 自定义错误处理:用app注册
def handle_error(e):
return 'ooooooo bad', 401
app.register_error_handler(401, handle_error)