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

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

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

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

然后给打一波广告。测试交流群:497127619。

Python装饰器实现几类验证功能做法的更多相关文章

  1. Python装饰器实现几类验证功能做法(续)

    :昨天聊了一下构造.今天试了一下.感觉昨天聊的还是不够细化.今天结合代码实现,加以一点补充. 首先观察下面这个例子 from functools import wrapsdef decorator(f ...

  2. python装饰器2:类装饰器

    装饰器1:函数装饰器 装饰器2:类装饰器 装饰器3:进阶 本文是装饰器相关内容的第二篇,关于类装饰器. "类装饰器"有两种解读方式:用来装饰类的装饰器:类作为装饰器装饰其它东西.你 ...

  3. 装饰器实现session登陆 验证功能

    装饰器 登陆验证功能 1.装饰器模板 from django.shortcuts import render, redirect, HttpResponse from django.conf impo ...

  4. python 装饰器,传递类以及参数

    #!/usr/bin/env python # coding=utf- import time #import redis class RedisLock(object): def __init__( ...

  5. python 装饰器 第二步:扩展函数的功能(不修改原函数)

    # 第二步:扩展函数的功能(不能修改原函数) # 用于扩展基本函数的函数 # 把一个函数(eat函数)作为一个整体传给另外一个函数(kuozhan函数) # 这个函数(kuozhan函数)用形参fun ...

  6. python 装饰器、递归原理、模块导入方式

    1.装饰器原理 def f1(arg): print '验证' arg() def func(): print ' #.将被调用函数封装到另外一个函数 func = f1(func) #.对原函数重新 ...

  7. python装饰器,迭代器,生成器,协程

    python装饰器[1] 首先先明白以下两点 #嵌套函数 def out1(): def inner1(): print(1234) inner1()#当没有加入inner时out()不会打印输出12 ...

  8. Python装饰器实现类Java注解功能

    最近想用Python写一个简单生成器,类似指定类型和范围,返回指定列表: 比如想要  0 ~ 3 的整数,则 我只需要指定: 最小:0, 最大:3, 步长:1 则返回一个 [0,1,2,3] 的列表 ...

  9. Python 装饰器装饰类中的方法

    title: Python 装饰器装饰类中的方法 comments: true date: 2017-04-17 20:44:31 tags: ['Python', 'Decorate'] categ ...

随机推荐

  1. 【转】如何实现Flex页面跳转

    其实对于这个题目是不恰当的,因为flex中是没有页面这个概念的,页面在flex里面其实就是一个个的Canvas,vbox,hbox等等之类的东西,看到的不同页面的切换,就是这些元素一层层的堆积,或者替 ...

  2. WPF之路二: button添加背景图片点击后图片闪烁问题

    在为button添加背景图片的时候,点击后发现图片闪烁,我们仔细观察,其实Button不仅仅只是在点击后会闪烁,在其通过点击或按Tab键获得焦点后都会闪烁,而通过点击其他按钮或通过按Tab键让Butt ...

  3. POPTEST老李谈Debug和Release的区别(c#)2

    二.哪些情况下 Release 版会出错 有了上面的介绍,我们再来逐个对照这些选项看看 Release 版错误是怎样产生的 1. Runtime Library: 2. 优化:这类错误主要有以下几种: ...

  4. MyBetis3.2框架技术

    1.1    MyBatis介绍 MyBatis 世界上流行最广泛的基于SQ语句的ORM框架,由Clinton Begin 在2002 年创建,其后,捐献给了Apache基金会,成立了iBatis 项 ...

  5. MySQL学习笔记(三)—索引

    一.概述 1.基本概念       在大型数据库中,一张表中要容纳几万.几十万,甚至几百万的的数据,而当这些表与其他表连接后,所得到的新的数据数目更是要大大超出原来的表.当用户检索这么大量的数据时,经 ...

  6. mysql视图 更新中的问题

    mysql view 类型 mysql的视图有三种类型:merge.temptable.undefined.如果没有ALGORITHM子句,默认算法是UNDEFINED(未定义的). 算法会影响MyS ...

  7. MongoDB基础教程系列--第七篇 MongoDB 聚合管道

    在讲解聚合管道(Aggregation Pipeline)之前,我们先介绍一下 MongoDB 的聚合功能,聚合操作主要用于对数据的批量处理,往往将记录按条件分组以后,然后再进行一系列操作,例如,求最 ...

  8. Java环境变量详解

    自己总结些再加抄点: 安装JDK后要配置环境变量,主要有三个: 1 JAVA_HOME ->为JDK的安装目录,如:F:\JAVA\jdk1.6.0_04 2 CLASSPATH ->到哪 ...

  9. 前馈神经网络-反向传播(Back Propagation)公式推导走读

        构造:输入神经元个数等于输入向量维度,输出神经元个数等于输出向量维度.(x1=(1,2,3),则需要三个输入神经元)   一 前向后传播   隐层:

  10. T-SQL几个简单的操作

    视图 咱们先来了解下视图到底是什么意思,顾名思义,用简单的视觉方式展现复杂的内容 有什么功能呢, 各位应该还记得咱们之前练习的那个小小的数据库superise,里面有这么四张表,分别是: studen ...