如何将装饰器应用到flask中的所有蓝图url

问题描述 投票:0回答:4

我有一个蓝图和一些url函数,

admin_bp = Blueprint('admin', __name__)

@admin_bp.route('/dashboard', methods=['GET', ])
@flask_login.login_required
def dashboard():

    context = {}

    page = 'admin/dashboard.html'
    return render_template(page, **context)

@admin_bp.route('/deny', methods=['GET', ])
@flask_login.login_required
def deny():
    return 'hey bro you dont belong here'

我不想为该蓝图下的所有 url 函数复制粘贴

@flask_login.login_required
装饰器。有没有更好的方法可以为所有蓝图网址应用装饰器?

python flask flask-login
4个回答
18
投票

您可以添加

before_request()
作为将在视图中的每个请求之前运行的函数。

然后,您将需要添加装饰器以向

before_request
函数注入附加功能。您需要导入
login_required
装饰器以确保每个端点都需要登录用户。该装饰器是
flask_login
库的一部分。

由于您的视图看起来像是管理员的一部分,因此我还建议向您的

before_request
函数添加一个自定义装饰器,例如
@role_required('admin')
之类的东西。该装饰器的功能将存在于其他地方并被导入。

@admin_bp.before_request
@login_required
def before_request():
    """ Protect all of the admin endpoints. """
    pass 

2
投票

子类

Blueprint
并重写
route
方法。

import flask

class MyBlueprint(flask.Blueprint):
  def route(self, rule, **options):
    def decorator(f):
      # these lines are copied from flask.Blueprint.route
      endpoint = options.pop("endpoint", f.__name__)
      self.add_url_rule(rule, endpoint, f, **options)

      # At this point flask.Blueprint.route simply returns f.
      # But you can nest a decorator.
      def inner(*args, **kwargs):
        # stuff you want to do before each request goes here
        try:
          result = f(*args, **kwargs)
          # stuff you want to do on successful responses (probing status, headers, etc.) goes here
        except Exception as e:
          # stuff you want to do on error responses goes here
          raise
      return inner

现在在您的蓝图中使用新的子类:

-v1_blueprint = Blueprint('v1', __name__)
+v1_blueprint = MyBlueprint('v1', __name__)

个别路线无需更改。

这种方法的缺点是它从 Flask 内部复制代码。如果

flask.Blueprint.route
的实现在未来版本中发生更改,则在升级 Flask 时需要将 MyBlueprint 与其同步。


0
投票

先检查一下用户如何:

from flask.ext.login import current_user


@admin_bp.before_request
def check_user():
    if not current_user.is_authenticated():
        abort(401)

# your other functions without `@flask_login.login_required`

0
投票

添加在@kartik 答案之上

我尝试做同样的事情,特别是我需要一个装饰功能(与@Robert不同)答案,它使用

before_request
提供了一个解决方案,它是一个中间件,并且不包装函数本身,因为我的问题需要装饰来分析代码.

我解决了这个问题,没有从 Flask 的

Scaffold
类中复制代码。
我们只需要覆盖我们的包装函数并将其传递给 Flask 创建的装饰器。
当然
my_decorator
可以是任何你需要的装饰器。

import typing as t
from functools import wraps

from flask import Flask


def my_decorator(f):
    @wraps(f)
    def inner(*args, **kwargs):
        print(f"Entering {f.__name__}")
        result = f(*args, **kwargs)
        print(f"Finished {f.__name__}")
        return result

    return inner


class MyFlask(Flask):
    def route(self, rule: str, **options: t.Any) -> t.Callable:
        route_decorator = super().route(rule, **options)

        def inner(f):
            new_f = my_decorator(f)
            return route_decorator(new_f)

        return inner


app = MyFlask("app")


@app.route("/test")
def test():
    print("In test")
    return {}


app.run()

然后,运行:

import requests
requests.get("http://localhost:5000/test")

打印

Entering test
In test
Finished test
© www.soinside.com 2019 - 2024. All rights reserved.