Python/Django装饰器可以访问哪些信息?

1 投票
1 回答
1077 浏览
提问于 2025-04-17 14:19

我正在对我们的代码进行一些重构。之前有一个 @render_to_json 装饰器,它内部调用了一个 is_logged_in 方法。现在我打算把这个方法去掉,改为直接添加一个明确的 @login_required 装饰器。

问题是,我们的一些方法是通过 AJAX 调用的,它们期待返回一个 JSON 格式的响应,比如 {"status": "logged_out"},然后根据这个响应进行处理。所以我需要修改我们的 login_required 方法,让它能检查以下两种情况之一。

  • 这个方法是通过 AJAX 调用的吗?可以通过检测 XMLHttpRequest 这个头来判断。
  • 这个请求是否也调用了 render_to_json 装饰器?我的想法是,如果 login_required 方法知道这个请求是期待 JSON 响应的,那么它就可以返回 JSON 数据,否则就正常重定向。

更新 添加第三个选项。

  • 在每个使用 @render_to_json 装饰器的方法中,@login_required 装饰器都是在它之前被引用的。如果用户未登录,@login_required 方法会返回 HttpResponseRedirect。那么在 @render_to_json 方法中,我该如何检查 login_required 方法的返回类型,并做出相应的处理呢?

大家有什么想法?有没有问题?

我还想补充一下,我对 Python 也比较陌生,可能会漏掉一些基础的东西。如果是这样,请帮我学习一下?

更新

我打算把这两个装饰器加上来以供参考。

def render_to_json(fn):

    @wraps(fn)
    def inner(request, *args, **kwargs):
        result = fn(request, *args, **kwargs)
        return HttpResponse(json.dumps(result), mimetype='application/json')

    return inner

def login_required(func):
    @wraps(func)
    def _decorator(request, *args, **kwargs):
        if not is_logged_in(request):
            from apps.core.extendedLogging import ExtendedLogging
            ExtendedLogging.log("In login req'd: it appears that the user is not logged in", request)
            request.session['login_referrer_uri'] = request.build_absolute_uri()
            return HttpResponseRedirect(settings.LOGIN_URL)
        return func(request, *args, **kwargs)
    return _decorator

1 个回答

1

装饰器可以访问“被装饰对象”接收到的所有数据:

def method_decorator(operation):
    """
    On this case operation = view_method
    """
    def wrapper(*args, **kwargs):
        """
        Receives all arguments the requested operation would receive
        """
            request = args[0]
            param = args[1]
            more_param = args[2]

            return operation(request, param, more_param)
    return wrapper

@method_decorator
def view_method(request, param, more_param):
    #something

所以,当你调用“view_method”时,首先会经过“method_decorator”,然后才会调用“view_method”。

在装饰器中,你可以检查你需要的任何信息,并传递这些新信息,比如:

def wrapper(*args, **kwargs):
        ...
        if some_condition:
            request.new_content = new_content
        return operation(request, param, more_param)
return wrapper

这样,你就可以在请求的 view_method 中接收到 new_content:

@method_decorator
def view_method(request, param, more_param):
    new_content = request.new_content

希望现在 view_method 知道该怎么做了。

你还可以通过同样的方法将数据从一个装饰器传递到另一个装饰器。

撰写回答