用装饰器封装Flask-WTF表单验证逻辑

Don't repeat yourself

在使用Flask-WTF的时候,常会用下面这样的代码来验证表单数据的合法性:

 1from flask import Flask
 2
 3app = Flask(__name__)
 4
 5@app.route('/', methods=['GET', 'POST'])
 6def index():
 7    form = TestForm()
 8    # 判断是否合法
 9    if not form.validate_on_submit():
10        return 'err', 400
11    # 主要逻辑

对于有很多提交接口的项目来说,需要在每个路由下写相同的的逻辑,造成了大量的代码重复。在Flask-Login中,要把一个路由设置为登录后才能访问,只需要在路由上加一个@login_required装饰器,不需要额外的代码。能不能像Flask-Login一样,用装饰器来封装对表单的验证逻辑呢?

01

实现表单验证装饰器

由于不同路由使用的表单类不一样,所以需要为装饰器传入一个表单类参数,并且在路由函数中需要用到表单中的值,所以还需要将验证通过的表单传给路由函数。

上代码:

1def validate_form(self, form_cls):
2    def decorator(fn):
3        @wraps(fn)
4        def wrapper(*args, **kwargs):
5            if not form.validate_on_submit():
6                return 'error', 400
7            return fn(form, *args, **kwargs)
8        return wrapper
9    return decorator

使用方式如下:

1@validate_form(TestForm)  # 需要传入要验证的表单类
2@app.route('/', methods=['GET', 'POST'])
3def index(form):
4    # 执行到这里说明表单验证通过

经过在项目中的应用,发现装饰器还是有一些缺陷:

- 无法自定义处理非法表单的逻辑

- 不支持get方式提交的表单(查看validate_on_submit()源码可知其只支持对post和put方式提交的表单进行验证)

02

丰富一下

要自定义处理非法表单的逻辑,需要增加一个可以传入自定义逻辑的接口。表单非法时接口的返回往往是一致的,所以我们为所有应用装饰器的路由传入一个统一的处理逻辑。将装饰器封装在一个类中,在类中添加一个配置处理逻辑的方法。

 1from functools import wraps
 2
 3from flask import request
 4
 5
 6class FormValidator(object):
 7
 8    def __init__(self, error_handler=None):
 9        self._error_handler = error_handler
10
11    def validate_form(self, form_cls):
12        def decorator(fn):
13            @wraps(fn)
14            def wrapper(*args, **kwargs):
15                if not form.validate_on_submit() and self._error_handler:
16                    return self._error_handler(form.errors)
17                return fn(form, *args, **kwargs)
18            return wrapper
19        return decorator
20
21    def error_handler(self, fn):
22        self._error_handler = fn
23        return fn

error_handler也是一个装饰器,被它修饰的方法就是处理非法表单的方法。

1@form_validator.error_handler
2def error_handler(errors):
3    return jsonify({'errors': errors}), 400

接下来支持get方法,在flask中,我们可以通过request.args来获取到get方法提交的参数。思路是用获取到的参数生成一个表单类的实例,然后就可以通过调用表单类的validate()方法来判断是否合法了。修改validate_form装饰器:

 1def validate_form(self, form_cls):
 2    def decorator(fn):
 3        @wraps(fn)
 4        def wrapper(*args, **kwargs):
 5            if request.method == 'GET':
 6                form = form_cls(formdata=request.args)
 7            elif request.method in ('POST', 'PUT'):
 8                form = form_cls()
 9            else:
10                return fn(*args, **kwargs)
11            if not form.validate() and self._error_handler:
12                return self._error_handler(form.errors)
13            return fn(form, *args, **kwargs)
14        return wrapper
15    return decorator

大功告成!使用上面的装饰器,就可以免除在路由函数中重复写表单验证逻辑,并且同时支持put、post和get方法提交的表单。

03

开箱即用

笔者已经把上面的代码封装成了一个库发布到了PyPI,想直接用的朋友可以使用`pip install flask-wtf-decorators`安装,项目源码也已经发布到Github。

原文发布于微信公众号 - Python私房菜(python-fans)

原文发表时间:2018-04-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏ascii0x03的安全笔记

SEED缓冲区溢出实验笔记

缓冲区溢出实验(Linux 32位) 参考教程与材料:http://www.cis.syr.edu/~wedu/seed/Labs_12.04/Software...

4505
来自专栏我的博客

Git的一些使用笔记

从拉远端代码到本地仓库 git pull origin master:master

33024
来自专栏老九学堂

编码秘籍,Java程序员必看的调试技巧

调试可以帮助我们识别和解决应用程序缺陷,老九君下面介绍的调试方法基本都是通用的,有了下面的这些技巧在开发中会让我们在编程中事半功倍,避免浪费时间! 1.条件断点...

4056
来自专栏我的技术专栏

《Go in action》读后记录:Go的并发与并行

983
来自专栏北京马哥教育

神技能:Python控制键盘鼠标

上面提到的子包都已被引入到pynput库中。要使用上面的子包,从pynput中引入即可。

2520
来自专栏vue学习

10、less的引用及公共变量的抽离

less是什么自然不用多言,乃一个css预编译器,可以扩展css语言,添加功能如如允许变量(variables),混合(mixins),函数(functions...

1141
来自专栏进击的君君的前端之路

AJAX

2615
来自专栏逸鹏说道

Git异常:Cannot delete the branch 'test&' which you are currently on

GitHub实战系列汇总:http://www.cnblogs.com/dunitian/p/5038719.html ————————————————————...

2986
来自专栏偏前端工程师的驿站

让VIM支持Python2 by update-alternatives

前言  Ubuntu 16+中$ sudo apt install vim所安装的vim只支持Python3,但很多插件如YCM和powerline均需要Pyt...

2758
来自专栏从零开始学自动化测试

python测试开发django-4.获取url参数和name的作用

如打开博客园按时间分类标签页【https://www.cnblogs.com/yoyoketang/archive/2018/10.html】,里面是时间参数是...

771

扫码关注云+社区

领取腾讯云代金券