装饰器不会将对象返回给 Python 中的类方法

标签

所以我试图将一个对象从装饰器返回到我的类的方法。基本上我正在做一个使用装饰器方法的 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'

es

我将把它分成,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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章