Python 从Flask应用程序中的外部应用程序上下文访问应用程序对象

Python 从Flask应用程序中的外部应用程序上下文访问应用程序对象,python,api,flask,key,config,Python,Api,Flask,Key,Config,如果请求头中的api键无效(或未指定),我已将路由函数的修饰符设置为中止(401) 所以我在我的主模块(名为app)的一个专用文件中定义了这个函数,它被导入到我的所有蓝图定义中。但是从这个文件中,我无法访问app对象来加载存储在config中的API密钥。我试图使用当前的应用程序,但我有一个错误告诉我不能在应用程序上下文之外使用它。我无法直接从应用程序模块导入应用程序对象,因为它会导致循环导入 如果我在\uuuu init\uuuuuu.py文件中声明decorator,我将面临相同的问题:我将

如果请求头中的api键无效(或未指定),我已将路由函数的修饰符设置为中止(401)

所以我在我的主模块(名为app)的一个专用文件中定义了这个函数,它被导入到我的所有蓝图定义中。但是从这个文件中,我无法访问app对象来加载存储在config中的API密钥。我试图使用当前的应用程序,但我有一个错误告诉我不能在应用程序上下文之外使用它。我无法直接从应用程序模块导入应用程序对象,因为它会导致循环导入

如果我在
\uuuu init\uuuuuu.py
文件中声明decorator,我将面临相同的问题:我将无法从应用程序模块导入require\u apikey函数,因为蓝图模块已导入应用程序模块中进行蓝图注册

我想我的设计有一些问题。你能指出我的缺点并帮我修复它吗

我的项目目录如下所示:

project/
 - run.py
 - app/
 - - __init__.py
 - - player.py
 - - require_apikey.py
以下是这些文件的内容:

# run.py
from app import app

if __name__ == "__main__":
    app.run()
设置你的应用程序 在项目的
\uuuu init\uuuu
文件中初始化您的
应用程序
非常简单,但当项目的规模变得更大时也非常有限(而且由于您正在使用路线蓝图,我猜您的项目已经足够大了)

在本例中,初始化
app
的推荐方法是使用,它基本上是一个创建并返回
app
实例的函数

这里有一个关于树木生存的快速例子(可能不是你能找到的最好的例子,但应该是这样的):

#myapp/application/setup.py
从烧瓶进口烧瓶
从.application.extensions导入数据库
def create_app():
app=烧瓶(名称)
app.config.from_对象(“myapp.config.config”)
#初始化扩展
db.init_应用程序(应用程序)
使用app.app_context():
db.create_all()
#登记蓝图
从myapp.player导入播放器
应用程序注册蓝图(播放器,url前缀=“/player/”)
返回应用程序
#myapp/application/extensions.py
从flask_sqlalchemy导入sqlalchemy
#在单独的文件中定义全局扩展名,以便可以从
#代码中的任何其他位置,而不创建循环导入
#在“create_app”函数中进行适当的初始化
db=SQLAlchemy()
#myapp/application/app.py
从.setup导入创建应用程序
app=create_app()
#myapp/uuuu init_uuuuu.py
从.application.app导入应用程序
#run.py
从myapp导入应用程序
如果名称=“\uuuuu main\uuuuuuuu”:
app.run()
这就是您项目的层次结构。此时,您有一个初始化
app
变量的
myapp/application/app.py
,您可以从任何地方导入,而不必担心导入循环

在调用view函数之前检查标题 有了我建议的arboresence,并且考虑到您相应地更新了导入,您的装饰师现在应该按照预期工作了。 但是,如果我告诉你,Flask提供了一种方法来做你想做的事情,而不需要实现装饰器呢?这才是关键所在。 这是一个您可以编写的特殊函数,它将在应用程序上的每个请求之前在应用程序上下文中调用

来自myapp.application.app导入应用程序
@请求前的应用程序
def require_apikey():
if request.headers.get(“api密钥”)!=API_密钥:
中止(401,“无效API密钥”)
现在的问题是,这个函数将为您定义的每个端点调用,而这可能不是您想要的。但是不要担心,您也可以在请求之前定义一个
函数来附加到特定的蓝图

#myapp/my_blueprint.py
从myapp.tools导入需要\u apikey
我的蓝图=蓝图(…)
my_blueprint.before_request=require_apikey

谢谢您的回答,这超出了我的预期。我没有时间回答之前的问题,但我设法得到我将修复我的初始问题,只需在我的decorator函数中设置
API_KEY
(它在视图函数中,因此
current_app
使用适当的应用上下文实例化)。感谢您解释应用程序工厂的用途。我已经看过了,但从来都不明白为什么需要它。现在很清楚了。虽然现在我不需要它来处理这个特殊的问题,但我打赌它以后会有用的。请求前的
@真是太棒了。
# app/__init__.py

from flask import (
    Flask,
    render_template,
    jsonify)

from app.db import db

from app.player import player

app = Flask(__name__)
app.config.from_object("app.config.Config")
app.register_blueprint(player, url_prefix="/player/")

db.init_app(app)

with app.app_context():
    db.create_all()
# app/player.py

from flask import (
    Blueprint,
    request,
    abort,
    jsonify)

from app import require_apikey

from app.models.player import Player

from app.db import db

player = Blueprint(__name__, __name__)

@player.route('/', methods=["GET", "POST"])
@require_apikey
def root():
    # Do some stuff
# app/require_apikey.py

from functools import wraps
from flask import (
    request,
    abort,
    current_app
)

API_KEY = current_app.config["SECRET_KEY"]

def require_apikey(view_function):
    @wraps(view_function)
    def decorated_function(*args, **kwargs):
        if request.headers.get("api-key") and request.headers.get("api-key") == API_KEY:
            return view_function(*args, **kwargs)
        else:
            abort(401, "Invalid API key")
    return decorated_function