共计 8914 个字符,预计需要花费 23 分钟才能阅读完成。
Sanic 和 Flask 简要概述
"""
Flask 常用
Sanic 和 Flask 很像,于是按着 Sanic 官方文档学了一下,对比 Flask 学习并做下笔记, 回顾一下
"""
Flask:轻量级 Web 框架,三方组件齐全,用时安装,扩展灵活度高。Sanic: 和 Flask 特别像,基础语法,基础命名特别相似(使用几乎差不多)。Sanic 是基于 Uvloop(没用过,了解即可,windows 不支持)实现,具有 异步 - 非阻塞的特性(网上也有说 Sanic 可以通过一些操作后,可以在 Windows 环境下使用,我试了貌似不行)(Linux 下运行才会具有最好的性能表现)python3.5+ 原生支持原生协程 async+await,这也可以在 sanic 的视图中用到,下面会介绍
安装
pip install flask
pip install sanic
入门程序(Flask vs Sanic)
Flask:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index(): # 注意这行
return 'hello' # 注意这行
if __name__=='__main__':
app.run('0.0.0.0', 1119, debug=True)
Sanic:from sanic import Sanic,response
app = Sanic(__name__)
@app.route('/')
def index(request): # 注意这行
return response.text('hello') # 注意这行
if __name__=='__main__':
app.run('0.0.0.0', 1119, debug=True)
使用 Sanic 的正确姿势 之 视图异步非阻塞
上面入门程序可以看到
flask 和 sanic 的路由对应的视图函数,是一样的
特殊的是:sanic 支持异步高性能,每个 def 前面 需要加上 async 即可
eg:
@app.route('/')
async def index(): # 以后写每个视图函数前面都要加上 async
return 'hello'
模板引擎(Flask VS Sanic)
Flask:
from flask import render_template
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html',
data=[dict(name='Tom', age=18), dict(name='Jerry', age=20)]
) # 这个模板渲染器是 flask 库中自带的,不需额外安装
Sanic:pip install sanic-jinja2 # 还需安装这个三方插件
from sanic import Sanic,response
from sanic_jinja2 import SanicJinja2 as sj # 导入就不说了,sj 只是命名来方便调用
app = Sanic(__name__)
tp = sj(app) # 注意这里,这个和 flask 三方应用注册是一个道理
使用方式 1:(Sanic 特有的装饰器方式)@app.route('/')
@tp.template('index.html') # 注意这行,以装饰器的方式渲染模板
async def index(request):
return { # 注意这里,返回值就是向模板填充的数据
'data': [dict(name='Tom', age=18),
dict(name='Jerry', age=8)
]
}
使用方式 2:(和 Flask 模板使用方法很像,很像)@app.route('/')
async def index(request):
return tp.render( # 这个方法代替了 上一种方法的装饰器
'index.html',
request, # 这个 request 参数,必须有
data = [# 模板渲染数据作为 redner()的 **kwargs 参数来传递
dict(name='Tom', age=18),
dict(name='Jerry', age=8)
]
)
if __name__=='__main__':
app.run('0.0.0.0', 1119, debug=True)
小结:Flask 的模板渲染机制是集成在 flask 库中,用 render_template 方法来直接渲染模板
并且,以方法参数的形式向模板传递数据
Sanic 的模板渲染机制是以第三方插件 sanic-jinja2 中的 SanicJinja2 组件来实现。使用时,需要先注册到 app 中,所接受的返回值,以装饰器的方式来渲染模板
个人看法:某种程度上来说,Sanic 更加细粒度的将 功能 以第三方应用的方式划分出来。即便如此,但我还是喜欢 flask 中 render_template 机制。
response 的各种返回方式对比分析(Flask VS Sanic)
Flask:
from flask import Markup, jsonify, send_file
@app.route('/')
def index():
# return 'hello' # Content-Type='text/plain'
# return Markup('<h1>hello</h1>') # 反转义
# return jsonify(dict(name='Tom')) # Content-Type-'application/json'
# return send_file('static/1.png') # 返回各种类型文件
# return 'Tom'.encode('utf-8') # 返回字节形式
下面是修改 状态码 和 headers 的方式:
# return render_template('home.html'), 220, {'a': 1}
格式:retrun 请求体, 状态码, 响应头
Sanic:
@app.route('/')
async def index(request):
# return response.text('hello') # Content-Type='text/plain'
# return response.html('<h1>hello<h1/>') # 代替反转义
# return response.json(dict(name='Tom')) # return response.redirect('/xxx')
# return await response.file('static/1.png') # 返回各种类型文件,注意有个 await
# return response.raw(b'Tom') # 返回原生字节类型数据
下面是修改 状态码 和 headers 的方式:
1. 如果返回的响应体 为 模板,就用下面的方式
@tp.template('index.html',status=220,headers={'name':'Tom'}) # 在装饰器参数里
2. 如果返回的响应体 为 非模板内容,就用如下方式
return response.text('hello',300,{'name':'Tom'})
格式: response.text(请求体,状态码,响应头)
小结:上面是针对 response 返回时,对各种数据类型的返回时可能用到的方式进行对比介绍。同时还对比讲述了 如何 修改 响应头 和 状态码
个人看法:Flask:1. response 各种变形返回方式 都封装了 flask 这个模块之中
2. response 的响应信息(状态码,响应头)等,通过 return 以 逗号或元组 方式构造返回.
eg: return 响应体,状态码,响应头
Sanic:
1. response 的各种变形返回方式 都封装了 sanic 模块的 response 中(分类更加明确)2. response 的响应信息(状态码,响应头)等,都放在函数中作为参数.
eg: response.xx(响应体,状态码,响应头)
request 的各种请求方式对比分析(Flask vs Sanic)
Flask:
from flask import request
request.method # 获取用户的请求方式: GET 或 POST
request.args # 接受 get 的 url 参数
request.form # 接受 post 的 form 表单 Content-Type='x-www-form-urlencoded'
request.json # 必须接受 Content-Type='application/json' 格式请求的数据
request.data # 请求数据对应的 Content-Type 除了表单(xxx-form)格式外,都可用此接受
request.values # 如有 form 和 url 联合参数,用这个接受
注:以上获取的对象都是 类字典对象,可以使用字典的 get('前端 name') 获取 value
此外:你还可以使用 to_dict()方法,就变成了纯种的字典 {k: v}
img = request.files.get() # 接受文件类型
img.save(img.filename) # filename 获取文件原始名,save 直接保存,默认当前路径。request.url # http://localhost:5000/login
request.path # /login
request.host # localhost:5000
request.host_url # http://localhost:5000/
request.remote_addr # 单纯获取 IP 地址
Sanic:
flask 中的 request 是导入进来的
而 sanic 中的 request 是在视图参数之中(参考 django)eg: def f(request) 就是这个意思
request.method # 同 Flask
rqeust.args # 同 Flask
request.form # 同 Flask
request.json # 请求若为表单 (xxx-form) 格式会报错,除了表单都可接受
request.body # 亲求若为表单 (xxx-form) 格式,则会出现一大堆 二进制信息,非表单都可接受
request.url # 同 Flask
request.path # 同 Flask
request.host # 同 Flask
request.ip # 同样单纯获取 IP,属性名和上面 flask 稍微有点不同
路由讲解(Flask vs Sanic)
Flask:
@app.route(
'login/<int:id>', # 路由参数解析并 自动转换 int 类型,冒号后为接受参数
methods=['GET,'POST'], # 建议全部大写
endpoint='sign', # 默认为下面的视图函数名,即 login,用于 url_for 反向解析
redirect_to='/xxx' # 重定向跳转,注意请求过来直接跳转,不进入下面视图函数
)
def login(id): # request 是默认必须传递的参数,id 是上面路由解析接收的
return f'{id+1}' # 路由参数 + python3.6 新增语法实现 f-string 接受参数自增
Sanic:
@app.route(
'login/<int:id>', # 路由参数解析并 自动转换 int 类型,冒号后为接受参数
methods=['GET,'POST'], # 建议全部大写
name='sign' # 同 flask 的 endpoint,用于 url_for 反向解析
)
def login(request, id): # request 是默认必须传递的参数,id 是上面路由解析接收的
return f'{id+1}' # 路由参数 + python3.6 新增语法实现 f-string 接受参数自增
Flask 模板 相关操作 (Flask)
注:由于 sanic 的 template 还不成熟,花式操作我也就没找,下面就只讲一下 flask 的常用模板操作
模板宏(macro):主要目的是为了前端代码的复用
定义 模板宏 就和 定义 python 的函数类似,或者你可以把 macro 看作 python 的 def
eg:
定义部分:可理解为函数定义
{% macro user(type, value) %}
<input type="{{type}}" value="{{value}}">
{% endmacro %}
调用部分:可理解为函数调用
{{user('text', '用户名') }}
{{user('password','密码') }}
全局模板自定义函数:视图文件.py 中定义:
@app.template_global()
def f(x):
return x**2
模板中调用:
{{f(5) }}
模板过滤器自定义函数:视图文件.py 中定义:
@app.template_filter()
def add(a, b):
return a+b
模板中调用:{{1 | add(2) }}
模板继承:(可理解为挖坑 与 填坑)父级模板: header.html
xxxxxxx 前面若干内容
{% block header %} # 这个 header,就是挖坑起的名,记住了
这里面就是你挖的坑,啥也不用写
{% endblock %}
xxxxxxx 后面若干内容
子级模板:content.html
{% extends 'header.html' %} # 必须写上这句,这是填坑的暗号。。{% block header %} # 这个名 header 是和上面挖坑的名一样
这里面就是你要填坑的数据
{% endblock %}
填完坑之后,这个 content.html 子级模板的内容 = 父级模板内容 + 填坑内容
模板代码块的导入(插入):作用还是 html 代码的复用
写好一个 html 代码块,放在 header.html 中
eg: <h1> Tom <h1/> # 注意,一个 html 文件中就写这一句就行
另一个文件:index.html 写入 如下代码:
xxxx 前面若干内容
{% include 'header.html' %} # 这一句就可把 那一个 html 内容全部插入进来
xxxx 后面若干内容
(中间件)钩子函数(Flask vs Sanic)
Flask:
@app.errorhandler(404) # 错误处理的钩子函数
def f(err):
return '出错了'
注:出现了异常,before_request 装饰的函数会立刻断掉,而 after_request 的会依然倒序执行
@app.before_request # 视图函数执行之前
def f(): return None 代表正常按顺序执行,xxx retrun 其他值,就不会进入视图函数,直接 response 返回
注:如果有多个 before_request,那么就 正序 装饰执行
@app.after_request # 视图执行之后,返回给 客户端之前
def f(res): 必须有个参数接受 response 对象,并且 return 回去
xxx
return res
注:如果有多个 before_request,那么就 倒序 装饰执行
如果仍然不明白执行顺序,看西面例子:eg:
@app.before_request
def req1():
print('请求来了 -1')
@app.before_request
def req2():
print('请求来了 -2')
@app.after_request
def res1(response):
print('请求走了 -1')
@app.after_request
def res1(response):
print('请求走了 -2')
@app.route('/lin')
def lin():
print('进入视图')
return '11'
结果 >>>
请求来了 -1
请求来了 -2
进入视图
请求走了 -2
请求走了 -1 # 这两个 response 是逆序的
Sanic:
from sanic.exceptions import NotFound
@app.exception(NotFound) # 错误处理的钩子函数
def f(request, err):
return response.text('出错了')
请求,返回中间件和 flask 大同小异,顺序有些区别,我直接上例子了
eg:
@app.middleware('request')
async def req(request):
print('请求来了 -1')
@app.middleware('request')
async def req(request):
print('请求来了 -2')
@app.middleware('response')
async def res(request, response1):
print('请求走了 -1')
return response1
@app.middleware('response')
async def res(request, response1):
print('请求走了 -2')
return response1
@app.route(xxx)
async def f(request):
print('进入视图')
return response.text('xx')
结果 >>>
请求来了 -1
请求来了 -2
进入视图
请求走了 -2 # 注意这里即可,只返回一个。下面不同点会详讲
总结:Flask 与 Sanic 中间件返回顺序对比
相同点:request 处理部分 : 装饰器代码 vs 执行流程 => 正序
response 处理部分: 装饰器代码 vs 执行流程 => 逆序
不同点:Sanic 只执行 最后一个 用装饰器注册的 response
Flask 执行顺序是 全部 用装饰器注册的 逆序返回的 response
蓝图(Flask vs Sanic)
蓝图使用三部曲:1. 新建目录和文件,创建蓝图对象
2. 在主 app 文件中, 导入蓝图对象
3. 注册蓝图对象
Flask:
1. 新建 /Admin/user.py,写入如下代码
from flask import Blueprint
user_bp = Blueprint('user_bp', __name__, url_prefix='/admin') # 增加 url 前缀
@user_bp.route('/user')
def f():
return 'admin-user'
2. 在 app 中导入 蓝图对象
from Admin.user import user_bp
3. 注册蓝图
app.register_blueprint(user_bp) # 这里也可以写 url 前缀, 如果写了就会覆盖上面写的
Sanic:
1. 新建 /Admin/user.py,写入如下代码
from sanic import Blueprint, response
user_bp = Blueprint(__name__, url_prefix='/admin')
@user_bp.route('/user')
async def f(request):
return response.text('admin-user')
2. 在 app 中导入 蓝图对象(同 Flask)from Admin.user import user_bp
3. 注册蓝图 (本来是和 flask 一样用 register_blueprint,后来版本更新改用 blueprint 注册)
app.blueprint(user_bp) # 这里也可以写 url 前缀, 如果写了就会覆盖上面写的
注:Flask 的蓝图对象,同 Flask 类似,都具有模板路径、静态文件路由 与 静态文件本地路径的配置
因此,蓝图实例化的时候,配置响应参数即可:template_folder = 'xxx' # 对应本地模板路径,默认 templates
static_folder = 'xxx' # 对应本地文件路径,默认 static
static_url_path = '/xxx' # 对应 url 路径,默认 /static
注 2:如果蓝图 和 app 的 模板或静态文件命名重复,那么会优先选择 app 下的模板或静态文件
CBV(Flask vs Sanic)
CBV(Class-Based-View):就是拿类当作视图(我们之前使用的函数作为视图)Flask 的 CBV 感觉没 FVB 好用,CBV 是 Django 的重点
Flask:
from flask import views
class UserView(views.MethodView):
methods = ['GET'] # 这里写 的是 允许的请求方式
decorators = [装饰器名,] # 全局装饰器顺序装饰,单独给函数加 @装饰器也可以
def get(self,*args, **kwargs):
return xx
def post(self, *args, **kwargs):
return xx
app.add_url_rule('/user',None,UserView.as_view('endpoint 名') )
Sanic:
from sanic.views import HTTPMethodView
class UserView(HTTPMethodView):
async def get(self, request):
return text('get')
async def post(self, request):
return text('post')
app.add_route(UserView.as_view(), '/') # Sanic 视图在前,路由在后。总结:讲道理,CBV 在这两个轻型框架感觉用的很笨拙。。还是很用 CBV 较好
Flask 的 flash(Flask)
flash 原理:服务器给 flash 设置了值,那么用户每请求一次,就会把 session 放到用户 cookie 中
(后面会提到 session 插件方法)
与此同时也把 flash 值记录在里面。flash 就相当于一跟管道
flash(): # 把值塞进管道
get_flashed_messages(): # 把值从管道取出来
from flask import flash, get_flashed_messages
# flash 是基于 session 来实现的,所以需要写一句:app.secret_key = '111'
@app.route('/lin')
def lin():
flash('666')
return redirect('/user')
@app.route('/user')
def user():
msg = get_flashed_messages()
print(msg) # 注意从 flash 取出来的是列表,因为你可以把不同数据多次 填入 flash
return ''
>>> [666]
正文完