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