Python装饰器实现几类验证功能做法实例


Posted in Python onMay 18, 2017

最近新需求来了,要给系统增加几个资源权限。尽量减少代码的改动和程序的复杂程度。所以还是使用装饰器比较科学

之前用了一些登录验证的现成装饰器模块。然后仿写一些用户管理部分的权限装饰器。

比如下面这种

def permission_required(permission):
  def decorator(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
      if not current_user.can(permission):
        abort(403)
      return f(*args, **kwargs)
    return decorated_function
  return decorator

def admin_required(f):
  return permission_required(Permission.SMY)(f)

调用权限的时候很好理解。直接仿写admin_required的格式就好了。然后每个页面入口用语法糖这样写: @admin_required

于是页面的入口权限就做好了。但是资源权限和页面权限不同。上面内容中提到的permission是写在model.py的静态内容里面的。

从封装来看,至少是看不出来哪个地方暴露了用户查询的方法(菜鸟水平下)。只能简单的看出来if判断的时候似乎使用了current_user这个变量的内置方法

但是current_user其实是一个第三方的包的内容,和登录模块引入的包相同,是一整套记录token信息的代码。详细内容太多。从这个地方出发去写,会go die

因为哪怕我知道其实调用的.can(permission)是model类里面定义的类方法。可是current_user是取了哪个部分的东西还是不清楚。

所以不管它。从头来梳理一下装饰器的内容。

首先一个简单的装饰器写法是很好理解的。比如原函数是这样写的:

def page():
  if user == 'admin':
    form = Form()
    
    if request.method=='POST':
      
      db.session.add(form)
      db.session.commit()
      flash("success")
      return 0

这当然是随便写的一个函数(明显有很多问题),只是用来表达一个过程。首先通过路由调用这个函数的时候,会先执行第一个if判断。这个判断即我们想要的验证内容

验证通过以后,说明用户可以访问这个页面,然后页面内容会渲染出来,交互功能也被允许……

那么装饰器,就是把这个if的功能提取出来了。那么原函数写成这样的形式:

@admin_check
def page():
    form = Form()

    if request.method=='POST':

      db.session.add(form)
      db.session.commit()
      flash("success")
      return 0

单从这个函数来说,这样写并没有任何好处,似乎本来一行代码搞定的问题,多用了几行代码。我们展开这个形式的完整代码看一下:

def admincheck(func):
  if user=='admin':
    return func
  
def page():
    form = Form()

    if request.method=='POST':

      db.session.add(form)
      db.session.commit()
      flash("success")
      return 0

page = admincheck(page())

上面的装饰器只是把page=admincheck这一句写成了@模式。

但是这种写法只能解决最基本的验证问题。也就是相对独立的入口验证。这个验证还没有拿到程序传递到page()函数当中的参数。也就是说,这个验证这么看起来没什么用处

不过机制是这样。接下来就可以研究怎样的做法是把路由传递过来的请求数据进行验证然后继续执行的了。

def admincheck(func):
  def inner(arg):
    if user == 'admin':
      if arg == 'false':
        abort(403)
    return func(arg)
  return inner

同样的,多个参数的时候,只需要把 def inner(arg)改写成def inner(arg1,arg2)

n个参数的时候,则写成def inner(*args,**kwargs) 这个需要注意一下。*args是元组,即('user',1);**kwargs是字典,即{'user':1}

同时写这两个形参的话,基本上就能处理所有传递进来的参数类型了。

当然。除此以外还有更复杂的装饰器写法。不过能处理传递过来的参数并且不影响被装饰函数的正常执行。基本上实现了之前的功能。

那么回过头来看示例当中的写法。最外层使用def permission_required(permission): 的意义,显然是想要实现复用。

def admin_required(f):
  return permission_required(Permission.SMY)(f)

上面的(permission)形参显然对应permission_required(Permission.SMY)中(Permission.SMY)这个参数。把这个参数的形参传递到方法体内部

这也是为什么要在装饰器decorator(f)外面再嵌套一层函数的原因——实现复用

于是之前这个写法的内容就很清晰了

def permission_required(permission):

#通过形参实现了一个装饰器类。对于不同针对性的装饰器,都可以调用这个函数的实现,而只需要做最小的改动(传递形参)
  def decorator(f):
#这个才是装饰器开始执行的第一步
    @wraps(f)


#这个装饰器实际上是为了保证函数的原始属性不发生改变。所谓原始属性,指的是__name__ 这种属性
    def decorated_function(*args, **kwargs):


#这个装饰器方法把原函数的形参继承了。因此实际上相当于在原函数开头增加了这个函数的内容
      if not current_user.can(permission):



#这个地方很明显。current_user是从内存中取(服务端),然后permission就会根据我们实际需要验证的permission进行形参到实参的转化
        abort(403)




#明显的异常处理,当然,403是一个粗暴的方法。更粗暴的方法,我会用redirect(url_for(logout))...
      return f(*args, **kwargs)



#结束判断,把参数传递给原函数(此处的f()即是原函数(更具体的权限验证装饰器),只是f是个丑陋的形参而已)
    return decorated_function
  return decorator

这样差不多就结束了。如果有人想补充,欢迎留言。

以上这篇Python装饰器实现几类验证功能做法实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Windows下安装python MySQLdb遇到的问题及解决方法
Mar 16 Python
Django原生sql也能使用Paginator分页的示例代码
Nov 15 Python
Python实现Kmeans聚类算法
Jun 10 Python
python爬虫获取多页天涯帖子
Feb 23 Python
Python实现的远程登录windows系统功能示例
Jun 21 Python
解决webdriver.Chrome()报错:Message:'chromedriver' executable needs to be in Path
Jun 12 Python
python lambda表达式在sort函数中的使用详解
Aug 28 Python
PyQT5 emit 和 connect的用法详解
Dec 13 Python
Python: 传递列表副本方式
Dec 19 Python
python3.8与pyinstaller冲突问题的快速解决方法
Jan 16 Python
python可以用哪些数据库
Jun 22 Python
pandas进行数据输入和输出的方法详解
Mar 23 Python
如何用itertools解决无序排列组合的问题
May 18 #Python
详解使用python的logging模块在stdout输出的两种方法
May 17 #Python
Python中正则表达式详解
May 17 #Python
python算法演练_One Rule 算法(详解)
May 17 #Python
浅谈pyhton学习中出现的各种问题(新手必看)
May 17 #Python
Python入门_学会创建并调用函数的方法
May 16 #Python
Python入门_浅谈逻辑判断与运算符
May 16 #Python
You might like
Http 1.1 Etag 与 Last-Modified提高php效率
2008/01/10 PHP
php 实现进制转换(二进制、八进制、十六进制)互相转换实现代码
2010/10/22 PHP
php接口和抽象类使用示例详解
2014/03/02 PHP
PhpStorm terminal无法输入命令的解决方法
2016/10/09 PHP
php从身份证获取性别和出生年月
2017/02/09 PHP
详谈phpAdmin修改密码后拒绝访问的问题
2017/04/03 PHP
JQuery 操作select标签实现代码
2010/05/14 Javascript
javascrip关于继承的小例子
2013/05/10 Javascript
Jquery easyUI 更新行示例
2014/03/06 Javascript
JavaScript实现动态创建CSS样式规则方案
2014/09/06 Javascript
JS实现当前页居中分页效果的方法
2015/06/18 Javascript
浅析Bootstrap表格的使用
2016/06/23 Javascript
原生的强大DOM选择器querySelector介绍
2016/12/21 Javascript
jQuery插件echarts实现的多柱子柱状图效果示例【附demo源码下载】
2017/03/04 Javascript
Vue异步组件使用详解
2017/04/08 Javascript
基于react框架使用的一些细节要点的思考
2017/05/31 Javascript
javascript 数据存储的常用函数总结
2017/06/01 Javascript
vue获取input输入值的问题解决办法
2017/10/17 Javascript
微信小程序项目实践之九宫格实现及item跳转功能
2018/07/19 Javascript
[55:47]DOTA2上海特级锦标赛C组小组赛#2 LGD VS Newbee第三局
2016/02/27 DOTA
Python读取键盘输入的2种方法
2015/06/16 Python
Python定义函数时参数有默认值问题解决
2019/12/19 Python
大学生毕业求职找工作的自我评价
2013/09/29 职场文书
护士毕业生自我鉴定
2014/02/08 职场文书
促销活动总结模板
2014/07/01 职场文书
机关党员四风问题个人整改措施
2014/10/26 职场文书
2015教师节师德演讲稿
2015/03/19 职场文书
警告通知
2015/04/25 职场文书
2015年护士节活动策划方案
2015/05/04 职场文书
毕业证明书
2015/06/19 职场文书
反四风问题学习心得体会
2016/01/22 职场文书
《鸡兔同笼》教学反思
2016/02/19 职场文书
Redis延迟队列和分布式延迟队列的简答实现
2021/05/13 Redis
Python访问Redis的详细操作
2021/06/26 Python
mongodb清除连接和日志的正确方法分享
2021/09/15 MongoDB
深入解析Apache Hudi内核文件标记机制
2022/03/31 Servers