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 相关文章推荐
教你用 Python 实现微信跳一跳(Mac+iOS版)
Jan 04 Python
mac安装scrapy并创建项目的实例讲解
Jun 13 Python
解决vscode python print 输出窗口中文乱码的问题
Dec 03 Python
python 利用pandas将arff文件转csv文件的方法
Feb 12 Python
详解python实现小波变换的一个简单例子
Jul 18 Python
python 下 CMake 安装配置 OPENCV 4.1.1的方法
Sep 30 Python
Python Selenium参数配置方法解析
Jan 19 Python
Python3+Selenium+Chrome实现自动填写WPS表单
Feb 12 Python
python数据分析:关键字提取方式
Feb 24 Python
解决pyecharts运行后产生的html文件用浏览器打开空白
Mar 11 Python
Python基于stuck实现scoket文件传输
Apr 02 Python
详解Django ORM引发的数据库N+1性能问题
Oct 12 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
php for 循环语句使用方法详细说明
2010/05/09 PHP
详解PHP实现执行定时任务
2015/12/21 PHP
Laravel Intervention/image图片处理扩展包的安装、使用与可能遇到的坑详解
2017/11/14 PHP
php处理多图上传压缩代码功能
2018/06/13 PHP
PHP应用跨时区功能的实现方法
2019/03/21 PHP
phpcmsv9.0任意文件上传漏洞解析
2020/10/20 PHP
Javascript图像处理思路及实现代码
2012/12/25 Javascript
javascript游戏开发之《三国志曹操传》零部件开发(一)让静态人物动起来
2013/01/23 Javascript
JS实现OCX控件的事件响应示例
2014/09/17 Javascript
nodejs下打包模块archiver详解
2014/12/03 NodeJs
js验证上传图片的方法
2015/05/12 Javascript
原生js实现模拟滚动条
2015/06/15 Javascript
学习jQuey中的return false
2015/12/18 Javascript
详解网站中图片日常使用以及优化手法
2017/01/09 Javascript
Angular2使用Angular CLI快速搭建工程(一)
2017/05/21 Javascript
Vue.js中的computed工作原理
2018/03/22 Javascript
inquirer.js一个用户与命令行交互的工具详解
2019/05/18 Javascript
Element Dialog对话框的使用示例
2020/07/26 Javascript
以视频爬取实例讲解Python爬虫神器Beautiful Soup用法
2016/01/20 Python
Python探索之爬取电商售卖信息代码示例
2017/10/27 Python
python 字典 按key值大小 倒序取值的实例
2018/07/06 Python
实时获取Python的print输出流方法
2019/01/07 Python
TensorFlow通过文件名/文件夹名获取标签,并加入队列的实现
2020/02/17 Python
解决c++调用python中文乱码问题
2020/07/29 Python
Python中实现一行拆多行和多行并一行的示例代码
2020/09/06 Python
Python-openpyxl表格读取写入的案例详解
2020/11/02 Python
Python 的 f-string 可以连接字符串与数字的原因解析
2021/02/20 Python
英国旅游额外服务市场领导者:Holiday Extras(机场停车场、酒店、接送等)
2017/10/07 全球购物
java程序员面试交流
2012/11/29 面试题
JSF界面控制层技术
2013/06/17 面试题
工程管理造价应届生求职信
2013/11/13 职场文书
庆七一活动方案
2014/01/25 职场文书
员工安全承诺书
2014/05/22 职场文书
独生子女证明范本
2015/06/19 职场文书
创业计划书之服装
2019/10/07 职场文书
浅谈 JavaScript 沙箱Sandbox
2021/11/02 Javascript