Python decorators and class inheritance
我试图使用装饰器来管理用户访问或不访问Web应用程序(在Google应用程序引擎上运行)中资源的方式。请注意,我不允许用户使用他们的Google帐户登录,因此在app.yaml中设置特定路由的特定访问权限不是一个选项。
我使用了以下资源:-布鲁斯·埃克尔的装饰指南-所以:get-class-in-python-decorator2-所以:python装饰和继承-所以:在python decorator中获取类
但是我还是有点困惑…
这是我的密码!在下面的示例中,当前用户是属于RequestHandler类的@property方法。它返回一个存储在数据存储中的用户(db.model)对象,该对象的级别为IntProperty()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class FoobarController(RequestHandler): # Access decorator def requiredLevel(required_level): def wrap(func): def f(self, *args): if self.current_user.level >= required_level: func(self, *args) else: raise Exception('Insufficient level to access this resource') return f return wrap @requiredLevel(100) def get(self, someparameters): #do stuff here... @requiredLevel(200) def post(self): #do something else here... |
但是,我的应用程序对不同类型的资源使用不同的控制器。为了在所有子类中使用@RequiredLevel修饰器,我需要将其移动到父类(RequestHandler):
1 2 3 4 5 | class RequestHandler(webapp.RequestHandler): #Access decorator def requiredLevel(required_level): #See code above |
号
我的想法是使用以下代码访问所有控制器子类中的decorator:
1 2 3 4 5 | class FoobarController(RequestHandler): @RequestHandler.requiredLevel(100) def get(self): #do stuff here... |
我想我对装饰师和类继承的知识已经达到极限了:)。有什么想法吗?
您的原始代码,有两个小的调整,也应该工作。对于这样一个简单的装饰器来说,基于类的方法似乎相当重要:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class RequestHandler(webapp.RequestHandler): # The decorator is now a class method. @classmethod # Note the 'klass' argument, similar to 'self' on an instance method def requiredLevel(klass, required_level): def wrap(func): def f(self, *args): if self.current_user.level >= required_level: func(self, *args) else: raise Exception('Insufficient level to access this resource') return f return wrap class FoobarController(RequestHandler): @RequestHandler.requiredLevel(100) def get(self, someparameters): #do stuff here... @RequestHandler.requiredLevel(200) def post(self): #do something else here... |
或者,您可以使用
1 2 3 4 5 | class RequestHandler(webapp.RequestHandler): # The decorator is now a static method. @staticmethod # No default argument required... def requiredLevel(required_level): |
。
原始代码不起作用的原因是RequiredLevel被假定为实例方法,它在类声明时(当您正在修饰其他方法时)将不可用,也不会从类对象中可用(将decorator放在RequestHandler基类上是一个很好的主意,结果是g decorator调用是很好的自我记录)。
您可能有兴趣阅读有关
另外,我喜欢在我的装潢师身上放一点样板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @staticmethod def requiredLevel(required_level): def wrap(func): def f(self, *args): if self.current_user.level >= required_level: func(self, *args) else: raise Exception('Insufficient level to access this resource') # This will maintain the function name and documentation of the wrapped function. # Very helpful when debugging or checking the docs from the python shell: wrap.__doc__ = f.__doc__ wrap.__name__ = f.__name__ return f return wrap |
在钻通StackOverflow,仔细阅读BruceEckel的装饰指南之后,我想我找到了一个可能的解决方案。
它涉及将decorator实现为父类中的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class RequestHandler(webapp.RequestHandler): # Decorator class : class requiredLevel(object): def __init__(self, required_level): self.required_level = required_level def __call__(self, f): def wrapped_f(*f_args): if f_args[0].current_user.level >= self.required_level: return f(*f_args) else: raise Exception('User has insufficient level to access this resource') return wrapped_f |
这就行了!使用f_args[0]对我来说似乎有点脏,如果我找到更漂亮的东西,我会编辑这个答案。
然后您可以按照以下方式在子类中装饰方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 | FooController(RequestHandler): @RequestHandler.requiredLevel(100) def get(self, id): # Do something here @RequestHandler.requiredLevel(250) def post(self) # Do some stuff here BarController(RequestHandler): @RequestHandler.requiredLevel(500) def get(self, id): # Do something here |
号
请随时评论或提出改进建议。