简体   繁体   English

在before_request信号触发之前,Flask命中装饰器

[英]Flask hit decorator before before_request signal fires

I'm using Flask and using the before_request decorator to send information about requests to an analytics system. 我正在使用Flask并使用before_request装饰器将有关请求的信息发送到分析系统。 I'm now trying to create a decorator that would prevent sending these events on a few routes. 我现在正在尝试创建一个装饰器,以防止在几条路线上发送这些事件。

The problem I'm running into is getting my decorator to get called before the before_request signal gets fired. 我遇到的问题是让我的装饰器在before_request信号被触发之前被调用。

def exclude_from_analytics(func):

    @wraps(func)
    def wrapped(*args, **kwargs):
        print "Before decorated function"
        return func(*args, exclude_from_analytics=True, **kwargs)

    return wrapped

# ------------------------

@exclude_from_analytics
@app.route('/')
def index():
    return make_response('..')

# ------------------------

@app.before_request
def analytics_view(*args, **kwargs):
    if 'exclude_from_analytics' in kwargs and kwargs['exclude_from_analytics'] is True:
       return

You can use the decorator to simply put an attribute on the function (in my example below, I'm using _exclude_from_analytics as the attribute). 您可以使用装饰器简单地在函数上放置一个属性(在我的示例中,我使用_exclude_from_analytics作为属性)。 I find the view function using a combination of request.endpoint and app.view_functions . 我使用request.endpointapp.view_functions的组合找到了view函数。

If the attribute is not found on the endpoint, you can ignore analytics. 如果在端点上找不到该属性,则可以忽略分析。

from flask import Flask, request

app = Flask(__name__)

def exclude_from_analytics(func):
    func._exclude_from_analytics = True
    return func

@app.route('/a')
@exclude_from_analytics
def a():
    return 'a'

@app.route('/b')
def b():
    return 'b'

@app.before_request
def analytics_view(*args, **kwargs):
    # Default this to whatever you'd like.
    run_analytics = True

    # You can handle 404s differently here if you'd like.
    if request.endpoint in app.view_functions:
        view_func = app.view_functions[request.endpoint]
        run_analytics = not hasattr(view_func, '_exclude_from_analytics')

    print 'Should run analytics on {0}: {1}'.format(request.path, run_analytics)

app.run(debug=True)

The output (ignoring static files...) 输出(忽略静态文件......)

Should run analytics on /a: False
127.0.0.1 - - [24/Oct/2013 15:55:15] "GET /a HTTP/1.1" 200 -
Should run analytics on /b: True
127.0.0.1 - - [24/Oct/2013 15:55:18] "GET /b HTTP/1.1" 200 -

I have not tested to see if this works with blueprints. 我没有测试过看蓝图是否有用。 Additionally, a decorator that wraps and returns a NEW function could cause this to not work since the attribute might be hidden. 此外,包装并返回NEW函数的装饰器可能导致此操作无效,因为该属性可能被隐藏。

Here's a variation on @Mark Hildreth's answer that does wrap and return a function: 这是@Mark Hildreth的回答 ,它包含并返回一个函数:

from functools import wraps
from flask import Flask, request, g

app = Flask(__name__)

def exclude_from_analytics(*args, **kw):
    def wrapper(endpoint_method):
        endpoint_method._skip_analytics = True

        @wraps(endpoint_method)
        def wrapped(*endpoint_args, **endpoint_kw):
            # This is what I want I want to do. Will not work.
            #g.skip_analytics = getattr(endpoint_method, '_skip_analytics', False)
            return endpoint_method(*endpoint_args, **endpoint_kw)
        return wrapped
    return wrapper

@app.route('/')
def no_skip():
    return 'Skip analytics? %s' % (g.skip_analytics)

@app.route('/skip')
@exclude_from_analytics()
def skip():
    return 'Skip analytics? %s' % (g.skip_analytics)

@app.before_request
def analytics_view(*args, **kwargs):
    if request.endpoint in app.view_functions:
        view_func = app.view_functions[request.endpoint]
        g.skip_analytics = hasattr(view_func, '_skip_analytics')
        print 'Should skip analytics on {0}: {1}'.format(request.path, g.skip_analytics)

app.run(debug=True)

The reason why it does not work quite as simply as I expected and hoped has to something do with the Flask context stack and the order in which callbacks are applied. 它不能像我预期的那样简单地工作,并希望与Flask上下文堆栈和应用回调的顺序有关。 Here is a timeline of method calls (based on some debug statements since removed): 这是方法调用的时间轴(基于自删除后的一些调试语句):

$ python test-flask-app.py
# Application Launched
DECORATOR exclude_from_analytics
DECORATOR wrapper
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

# REQUEST: /
DECORATOR app.before_request: analytics_view
> Should skip analytics on /: False
ENDPOINT no_skip
127.0.0.1 - - [14/May/2016 16:10:39] "GET / HTTP/1.1" 200 -

# REQUEST: /skip
DECORATOR app.before_request: analytics_view
> Should skip analytics on /skip: True
DECORATOR wrapped
ENDPOINT skip
127.0.0.1 - - [14/May/2016 16:12:46] "GET /skip HTTP/1.1" 200 -

I would prefer to set g.skip_analytics from within the wrapped function. 我更愿意在wrapped函数中设置g.skip_analytics But because that is not called until after the analytics_view @app.before_request hook, I had to follow Mark's example and set the _skip_analytics attr on the endpoint method loaded in what I'm calling the application (as opposed to request ) context which gets invoked only at launch. 但是因为只有在analytics_view @app.before_request挂钩之后才会调用@app.before_request ,所以我必须按照Mark的示例并在我调用的应用程序 (而不是请求 )上下文中加载的端点方法上设置_skip_analytics attr仅在发布时。

For more on flask.g and app context, see this StackOverflow answer . 有关flask.g和app上下文的更多信息,请参阅此StackOverflow答案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM