所以我试图将一个对象从装饰器返回到我的类的方法。基本上我正在做一个使用装饰器方法的 API:
def token_required(f):
@wraps(f)
def decorated(*args, **kwargs):
token = None
if 'x-access-token' in request.headers:
token = request.headers['x-access-token']
if not token:
return jsonify({'message' : 'Token is missing!'}), 401
try:
data = jwt.decode(token, app.config['SECRET_KEY'])
current_usuario = Usuarios.query.filter_by(usuarioid = data['usuarioid']).first()
except:
return jsonify({'message' : 'Token is invalid!'}), 401
return f(current_usuario, *args, **kwargs)
return decorated
问题是我需要在 UsuariosView 类的方法中获取 current_usuario,这就是我现在正在尝试的:
class UsuariosView(MethodView):
@token_required
def get(self, current_usuario, usuario_id = None):
if not current_usuario.admin:
return ({'message' : 'Not admin!'})
#do stuff
我没有取回 current_usuario,所以我无法访问他的属性 admin 并收到以下错误:
AttributeError: 'UsuariosView' 对象没有属性 'admin'
我将把它分成,1) 为什么你的代码失败,2) 一个可怕的黑客解决方案,它应该解决你当前的问题,为什么它会产生未来的问题,3) 一种不同的方法,它划分责任但可能不像我那样工作不知道你的设计细节。
1 - 实际上,您的装饰器获取一个值current_usuario
并将其绑定到您调用的方法的第一个参数。当该方法被调用时,所有其他参数随后被分配。这是一个更清楚的例子,说明正在发生的事情。
from functools import wraps
def assign_first(func):
@wraps(func)
def wrapper(*args, **kwargs):
# do stuff
first_argument = 10
return func(first_argument, *args, **kwargs)
return wrapper
class MyClass(object):
@assign_first
def first_method(self, first, second):
print(f"self: {self}")
print(f"first: {first}")
print(f"second: {second}")
输出:
>>> MyClass().first_method(100)
self: 10
first: <__main__.MyClass object at 0x7fdbdebd3400>
second: 100
装饰器绑定10
到方法的第一个参数(即self
),因此实例(应该绑定到self
)现在绑定到第二个参数(称为first
)。您收到该错误消息是因为您的UserView
对象已绑定到current_usuario
,并且没有admin
属性。
2)在你的情况下,你可以做一个非常错误的想法,让你的包装器绑定到一个关键字参数。但是,您将需要所有装饰方法才能知道它们需要该特定关键字参数并且仅使用关键字参数。
def assign_my_kwarg(func):
@wraps(func)
def new_func(*args, **kwargs):
# do stuff
return func(*args, my_kwarg=3, **kwargs)
return new_func
class MyClass(object):
@assign_my_kwarg
def second_method(self, *, my_kwarg, second=1): # a function that only takes keyword arguments
print(f"self: {self}")
print(f"my_kwarg: {my_kwarg}")
print(f"second: {second}")
跑步:
>>> MyClass().second_method(second=5)
self: <__main__.MyClass object at 0x7f679eccd400>
my_kwarg: 3
second: 5
因此,您可以对您的token_required
、 返回return f(*args, current_usuario=current_usuario, **kwargs)
并将您的方法签名更改为:def get(self, *, current_usuario, usuario_id = None):
。但它会非常脆弱且非常混乱。任何调用该方法并放入的人current_usuario
都会导致错误,因为它已经被赋值了。关键字的任何拼写错误都会导致错误。更改关键字的名称会导致无尽的头痛。
3)真正的问题是token_required
检查令牌和获取当前用户。您可以通过使用一个函数来获取引发自定义令牌错误的当前用户,然后使用一个装饰器来处理这些错误。
from functools import wraps
class BadTokenError(ValueError):
pass
class MissingTokenError(ValueError):
pass
def get_current_user():
token = get_token()
try:
data = jwt.decode(token, app.config['SECRET_KEY'])
return Usuarios.query.filter_by(usuarioid=data['usuarioid']).first()
except (SPECIFIC_ERROR, OTHER_SPECIFIC_ERROR): # do not catch MissingTokenError
msg = 'bad token'
raise BadTokenError(msg)
def get_token():
try:
token_name = 'x-access-token'
return request.headers[token_name]
except KeyError:
msg = 'missing token'
raise MissingTokenError(msg)
def catch_token_errors(func):
@wraps(func)
def func_with_handler(*args, **kwargs):
try:
return func(*args, **kwargs)
except (BadTokenError, MissingTokenError) as error:
return jsonify({'message': error.args[0]}), 401
return func_with_handler
class UsuariosView(MethodView):
@catch_token_errors
def get(self, usuario_id=None):
current_usuario = get_current_user()
if not current_usuario.admin:
return ({'message': 'Not admin!'})
这将更多地划分职责,但可能无法满足您的需求。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句