Don't repeat yourselfpython
在使用Flask-WTF的时候,常会用下面这样的代码来验证表单数据的合法性:git
from flask import Flask
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def index():
form = TestForm()
# 判断是否合法
if not form.validate_on_submit():
return 'err', 400
# 主要逻辑
复制代码
对于有不少提交接口的项目来讲,须要在每一个路由下写相同的的逻辑,形成了大量的代码重复。在Flask-Login中,要把一个路由设置为登陆后才能访问,只须要在路由上加一个@login_required装饰器,不须要额外的代码。能不能像Flask-Login同样,用装饰器来封装对表单的验证逻辑呢?github
因为不一样路由使用的表单类不同,因此须要为装饰器传入一个表单类参数,而且在路由函数中须要用到表单中的值,因此还须要将验证经过的表单传给路由函数。json
上代码:flask
def validate_form(self, form_cls):
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
if not form.validate_on_submit():
return 'error', 400
return fn(form, *args, **kwargs)
return wrapper
return decorator
复制代码
使用方式以下:app
@validate_form(TestForm) # 须要传入要验证的表单类
@app.route('/', methods=['GET', 'POST'])
def index(form):
# 执行到这里说明表单验证经过
复制代码
通过在项目中的应用,发现装饰器仍是有一些缺陷:函数
要自定义处理非法表单的逻辑,须要增长一个能够传入自定义逻辑的接口。表单非法时接口的返回每每是一致的,因此咱们为全部应用装饰器的路由传入一个统一的处理逻辑。将装饰器封装在一个类中,在类中添加一个配置处理逻辑的方法。post
from functools import wraps
from flask import request
class FormValidator(object):
def __init__(self, error_handler=None):
self._error_handler = error_handler
def validate_form(self, form_cls):
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
if not form.validate_on_submit() and self._error_handler:
return self._error_handler(form.errors)
return fn(form, *args, **kwargs)
return wrapper
return decorator
def error_handler(self, fn):
self._error_handler = fn
return fn
复制代码
error_handler也是一个装饰器,被它修饰的方法就是处理非法表单的方法。ui
@form_validator.error_handler
def error_handler(errors):
return jsonify({'errors': errors}), 400
复制代码
接下来支持get方法,在flask中,咱们能够经过request.args来获取到get方法提交的参数。思路是用获取到的参数生成一个表单类的实例,而后就能够经过调用表单类的validate()方法来判断是否合法了。修改validate_form装饰器:spa
def validate_form(self, form_cls):
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
if request.method == 'GET':
form = form_cls(formdata=request.args)
elif request.method in ('POST', 'PUT'):
form = form_cls()
else:
return fn(*args, **kwargs)
if not form.validate() and self._error_handler:
return self._error_handler(form.errors)
return fn(form, *args, **kwargs)
return wrapper
return decorator
复制代码
大功告成!使用上面的装饰器,就能够免除在路由函数中重复写表单验证逻辑,而且同时支持put、post和get方法提交的表单。
#开箱即用
笔者已经把上面的代码封装成了一个库发布到了PyPI,想直接用的朋友可使用pip install flask-wtf-decorators
安装,项目源码也已经发布到Github。