【问题标题】:Creating Flask-SQLAlchemy instance with metadata from Blueprint使用 Blueprint 中的元数据创建 Flask-SQLAlchemy 实例
【发布时间】:2019-04-21 11:56:46
【问题描述】:

TL;DR:如何使用蓝图中的metadata 对象来创建 Flask-SQLAlchemy 实例?我能看到提供声明性基础metadata 对象的唯一地方是在初始SQLAlchemy() 调用中。但是当我从 extensions.py 文件中的蓝图导入它时,蓝图的代码需要 db 对象,并且由于循环导入而导致加载失败。


我有几个模型类,我想在 Flask 内外使用它们。我正在使用 the declarative method 来执行此操作,并且我的应用程序设置为使用 App Factory 模型和蓝图。使用 SQLAlchemy 注册模型的方式是在创建 db 对象时使用 metadata 参数。在我的应用程序上下文中,在蓝图中而不是在主应用蓝图中声明 metadata 对象是有意义的。 (这是引用它的大部分代码所在的位置,包括用于最初填充数据库的非 Flask 实用程序脚本。)但是,从第二个蓝图导入模型类最终会以循环导入的形式出现。

$ flask db migrate
Error: While importing "my_app", an ImportError was raised:

Traceback (most recent call last):
  File "my_app/venv/lib/python3.7/site-packages/flask/cli.py", line 235, in locate_app
    __import__(module_name)
  File "my_app/my_app.py", line 1, in <module>
    from app import create_app
  File "my_app/app/__init__.py", line 7, in <module>
    from app.extensions import *
  File "my_app/app/extensions.py", line 10, in <module>
    from turf.models import metadata
  File "my_app/turf/__init__.py", line 1, in <module>
    from .routes import bp
  File "my_app/turf/routes.py", line 14, in <module>
    from app.extensions import db
ImportError: cannot import name 'db' from 'app.extensions' (my_app/app/extensions.py)

正如this general question on circular imports 中提到的蓝图,一个可行的解决方案是从第二个蓝图中的每个函数内部导入db 对象,从而在extensions.py 文件的初始化期间避开导入。但除了烦人之外,这感觉非常hacky。

理想情况下,我可以将我创建的 metadata 对象传递给 SQLAlchemy 的 init_app() 方法。这将一下子解决这个问题。不幸的是,init_app() 不接受 metadata 参数。初始化后是否有其他方法可以向 SQLAlchemy 实例注册元数据?还是我错过了声明性模型方法的其他一些关键元素?

我应该说它的非 Flask 部分工作得很好。我的实用程序脚本能够导入模型并使用它们将对象添加到数据库中。只有 Flask 导入给我带来了麻烦。

这是层次结构:

.
├── app
│   ├── __init__.py
│   └── extensions.py
└── turf
    ├── __init__.py
    ├── models.py
    └── routes.py

以及由于循环导入而失败的相关代码:

app/__init__.py:

from app.extensions import *

def create_app():
    app = Flask(__name__)

    with app.app_context():
        import turf
        app.register_blueprint(turf.bp)

        db.init_app(app)

app/extensions.py:

from turf.models import metadata

db = SQLAlchemy(metadata=metadata)

草皮/__init__.py:

from .routes import bp

草皮/models.py:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import MetaData

metadata = MetaData()
Base = declarative_base(metadata=metadata)

# All the turf models are declared in this file
class Boundary(Base):
    # ...etc...

草皮/routes.py:

from .models import *
from app.extensions import db

bp = Blueprint('Turf', __name__, url_prefix='/turf')

@bp.route('/')
def index():
    return render_template('turf/index.html')

【问题讨论】:

  • 请总结您的文章。这更像是写课本
  • 我已添加摘要,谢谢。如果我在初始化扩展之后注册蓝图,部分问题会得到解决。 Flask 将运行,但 flask_migrate (Alembic) 仍然抱怨。
  • (事实证明并非如此。flask run 仍然失败并出现相同的循环导入错误。不知道为什么它在重新加载后工作了一段时间然后停止了。)

标签: python flask sqlalchemy flask-sqlalchemy python-import


【解决方案1】:

事实证明,您可以在 extensions.py 文件中声明 MetaData 对象,然后将其导入蓝图中。我确信这会失败,因为在创建 db 对象之后,现在正在填充 metadata 对象,但我已经验证模型确实可用并且按预期工作。并且不再有循环依赖。实际上,我已将此部分拆分为自己的文件,以允许尽可能少地导入蓝图代码。

app/base.py:

from sqlalchemy import MetaData
from sqlalchemy.ext.declarative import declarative_base

metadata = MetaData()
Base = declarative_base(metadata=metadata)

app/extensions.py:

from flask_sqlalchemy import SQLAlchemy
from .base import metadata

db = SQLAlchemy(metadata=metadata)

草皮/models.py:

from app.base import Base

# All the turf models are declared in this file
class Boundary(Base):
    # ...etc...

这也回答了我在原始方法中遇到的另一个问题:如果我有第二个蓝图,它也有需要从非 Flask 代码中获得的模型对象,它会如何工作?现在,我创建了一个 Base 对象,可以根据需要使用它在不同的蓝图中实现新类。

不过,这种方法有一点小烦恼。在非 Flask DB 填充脚本中,我最初能够使用 from models import * 来引用包含模型的同级模块(文件)。这让我可以直接调用脚本,à la cd turf; python populate_db.py --arg。这不再有效,因为models.py 文件现在引用了不同的包app.extensions。因此,我必须使用此解决方法:

草皮/populate_db.py:

try:
    from .models import *
except:
    print("You have to run this from outside the 'turf' directory like so: $ python -m turf.populate_db [...]")
    sys.exit(1)

【讨论】:

    猜你喜欢
    • 2015-06-06
    • 1970-01-01
    • 2021-07-09
    • 2013-01-28
    • 2021-10-09
    • 1970-01-01
    • 2021-02-19
    • 2022-01-15
    相关资源
    最近更新 更多