【问题标题】:Flask hit decorator before before_request signal fires在 before_request 信号触发之前烧瓶命中装饰器
【发布时间】:2013-11-03 16:15:47
【问题描述】:

我正在使用 Flask 并使用 before_request 装饰器发送关于 对分析系统的请求。我现在正在尝试创建一个装饰器 防止在几条路线上发送这些事件。

我遇到的问题是让我的装饰器在 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

【问题讨论】:

  • 不是重复的,但可能会给你一些想法:stackoverflow.com/questions/14367991/…
  • 这就是我们目前正在做的事情,但是随着路由和代码库数量的增长,它绝对不是一个可扩展的解决方案。
  • 我的想法是让“exclude_from_analytics”装饰器在视图函数本身上放置一个属性,然后使用 Flask API 从端点获取视图函数并检查属性。
  • 仅供参考,app.route() 必须是最顶层的装饰器。否则你的包装函数永远不会被使用。现在你有这个:exclude_from_analytics(app.route(index)) - 如你所见,原始函数被传递给app.route()

标签: python flask decorator


【解决方案1】:

您可以使用装饰器在函数上简单地放置一个属性(在下面的示例中,我使用_exclude_from_analytics 作为属性)。我使用request.endpointapp.view_functions 的组合找到了视图函数。

如果在端点上找不到该属性,您可以忽略分析。

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)

输出(忽略静态文件...)

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 -

我尚未测试这是否适用于蓝图。此外,包装并返回 NEW 函数的装饰器可能会导致它不起作用,因为该属性可能被隐藏。

【讨论】:

    【解决方案2】:

    这是@Mark Hildreth's answer 的一个变体,它确实包装并返回一个函数:

    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)
    

    它没有像我预期和希望的那样简单地工作的原因与 Flask 上下文堆栈和应用回调的顺序有关。以下是方法调用的时间线(基于删除后的一些调试语句):

    $ 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 -
    

    我更愿意在wrapped 函数中设置g.skip_analytics。但是因为直到 analytics_view @app.before_request 钩子之后才会调用它,所以我必须按照 Mark 的示例并在我调用的端点方法上设置 _skip_analytics attr strong>application(相对于 request)上下文,仅在启动时调用。

    有关flask.g 和应用上下文的更多信息,请参阅this StackOverflow answer

    【讨论】:

      猜你喜欢
      • 2013-11-01
      • 2012-07-14
      • 1970-01-01
      • 2016-03-11
      • 2021-04-06
      • 2020-09-20
      • 1970-01-01
      • 2014-08-25
      • 2018-08-11
      相关资源
      最近更新 更多