如何避免使用flask,SQLAlchemy和declarative_base硬编码数据库路径

时间:2018-06-24 13:13:48

标签: python flask flask-sqlalchemy

Flask-SQLAlchemy建议使用Declarative模式来建立数据库连接。该示例对单个数据库路径进行硬编码,似乎应该避免这种情况,以便可以为单元测试,本地开发以及最终生产配置不同的数据库。

这是他们的数据库。py:

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False,
                                     autoflush=False,
                                     bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()

def init_db():
    # import all modules here that might define models so that
    # they will be registered properly on the metadata.  Otherwise
    # you will have to import them first before calling init_db()
    import yourapplication.models
    Base.metadata.create_all(bind=engine)

但是我一直无法弄清楚如何避免硬编码该数据库路径。

环境变量:

我可以用环境变量替换sqlite:///....路径。但这需要某种方式来使pytest与一组环境变量一起运行,而flask run使用另一组环境变量。我宁愿让单元测试跟踪自己的临时数据库。[2]我还希望创建create_app()的Flask应用程序采用配置变量的数据库路径。

在应用程序上下文中导入database.py

我认为我可以用current_app.config['DB']替换数据库路径,并在with app.app_context()内导入数据库。但是Model类需要导入Base,而我不能在应用程序上下文中将Base子类化。因此,当模型被加载时,它们被加载到应用程序上下文之外,这会引发运行时错误。

那么在带有单元测试的烧瓶应用程序中使用declarative_base的正确方法是什么?

感谢您的帮助!

[2]:如果那是我应该做的,那么我可以接受。但是当我也使用自己的配置变量配置flask应用程序时,在其中使用env vars似乎很麻烦。

1 个答案:

答案 0 :(得分:1)

我已经解决了我的问题。代替使用declarative_base类工厂:

database.py:

engine="someengine"
db_session=[make a session here]
Base = declarative_base()
Base.query = db_session.query

让我的模型导入Base

我只是遵循模式here。在database.py中,我使用

创建一个哑表
db = SQLAlchemy()

然后在create_app应用程序工厂的 init .py中,我可以使用app config变量初始化应用程序,并使用app初始化数据库。*

db.init_app(app)
with app.app_context():
    import site.models
    db.create_all()

模型继承自database.py的db.Model类。

from site.database import db
class MyModel(db.Model)

我可以通过将测试包装在夹具中来访问pytest测试中的数据库:

@pytest.fixture
def app():
    app = create_app({[testing config]}
    with app.app_context():
        g.db = db
        yield app
        g.db.session.remove()
        g.db.drop_all()

现在正在为我工​​作。也就是说,如果您认为我做错了,请告诉我!

*我认为SQLAlchemy对象一旦用init_app初始化后,就会存储指向仅在存在活动应用程序上下文时才存在的值的指针?那正确吗?这就是为什么仅调用db不会连接到应用程序数据库,而是在应用程序上下文中调用db的原因。