如何使用 SQLAlchemy 在 Flask 应用程序中初始化 Postgresql 数据库
How to initialize a Postgresql database in a Flask app with SQLAlchemy
Flask tutorial (and many other tutorials out there) 表明 engine
、db_session
和 Base
(declarative_metadata
的一个实例)都是在导入时创建的-时间。
这会产生一些问题,其中之一是数据库的 URI 在代码中被硬编码并且只计算一次。
一种解决方案是将这些调用包装在接受 app
作为参数的函数中,这就是我所做的。请注意 - 每次调用都会将结果缓存在 app.config
:
中
def get_engine(app):
"""Return the engine connected to the database URI in the config file.
Store it in the config for later use.
"""
engine = app.config.setdefault(
'DB_ENGINE', create_engine(app.config['DATABASE_URI'](), echo=True))
return engine
def get_session(app):
"""Return the DB session for the database in use
Store it in the config for later use.
"""
engine = get_engine(app)
db_session = app.config.setdefault(
'DB_SESSION', scoped_session(sessionmaker(
autocommit=False, autoflush=False, bind=engine)))
return db_session
def get_base(app):
"""Return the declarative base to use in DB models.
Store it in the config for later use.
"""
Base = app.config.setdefault('DB_BASE', declarative_base())
Base.query = get_session(app).query_property()
return Base
在init_db
中,我调用了所有这些函数,但仍然有代码味道:
def init_db(app):
"""Initialise the database"""
create_db(app)
engine = get_engine(app)
db_session = get_session(app)
base = get_base(app)
if not app.config['TESTING']:
import flaskr.models
else:
if 'flaskr.models' not in sys.modules:
import flaskr.models
else:
import flaskr.models
importlib.reload(flaskr.models)
base.metadata.create_all(bind=engine)
气味当然是我导入和创建所有模型所必须经历的箍。
上面代码的原因是,单元测试时,每次测试调用一次init_db
(在setup()
中,如suggested in the same tutorial),但只会在第一次执行导入, 因此 create_all
将仅在那个时间有效。
不仅如此,现在在应用程序运行期间共享一个会话,我在参数化负单元测试(即预期某种失败的参数化单元测试)中遇到问题:测试的第一个实例将触发失败(例如登录失败,请参阅 test_login_validate_input
in the tutorial)并正确退出,而所有后续将提前退出,因为 db_session
应该首先回滚。显然数据库初始化有问题。
初始化数据库的正确方法 (TM) 是什么?
我最终决定重构该应用,使其使用 Flask-SQLAlchemy。
简而言之,应用程序现在执行如下操作:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
db.init_app(app)
# ...
事后看来,这绝对是一种更简洁的方法。
一开始让我失望的是 tutorial 中的这个条目(粗体是我的):
Because SQLAlchemy is a common database abstraction layer and object relational mapper that requires a little bit of configuration effort, there is a Flask extension that handles that for you. This is recommended if you want to get started quickly.
我以某种方式将其解读为“使用 Flask-SQLAlchemy 扩展程序将允许您偷工减料,您可能最终会为此付费”。
这是非常早期的阶段,但到目前为止,在使用上述扩展的灵活性方面无需付出任何代价。
Flask tutorial (and many other tutorials out there) 表明 engine
、db_session
和 Base
(declarative_metadata
的一个实例)都是在导入时创建的-时间。
这会产生一些问题,其中之一是数据库的 URI 在代码中被硬编码并且只计算一次。
一种解决方案是将这些调用包装在接受 app
作为参数的函数中,这就是我所做的。请注意 - 每次调用都会将结果缓存在 app.config
:
def get_engine(app):
"""Return the engine connected to the database URI in the config file.
Store it in the config for later use.
"""
engine = app.config.setdefault(
'DB_ENGINE', create_engine(app.config['DATABASE_URI'](), echo=True))
return engine
def get_session(app):
"""Return the DB session for the database in use
Store it in the config for later use.
"""
engine = get_engine(app)
db_session = app.config.setdefault(
'DB_SESSION', scoped_session(sessionmaker(
autocommit=False, autoflush=False, bind=engine)))
return db_session
def get_base(app):
"""Return the declarative base to use in DB models.
Store it in the config for later use.
"""
Base = app.config.setdefault('DB_BASE', declarative_base())
Base.query = get_session(app).query_property()
return Base
在init_db
中,我调用了所有这些函数,但仍然有代码味道:
def init_db(app):
"""Initialise the database"""
create_db(app)
engine = get_engine(app)
db_session = get_session(app)
base = get_base(app)
if not app.config['TESTING']:
import flaskr.models
else:
if 'flaskr.models' not in sys.modules:
import flaskr.models
else:
import flaskr.models
importlib.reload(flaskr.models)
base.metadata.create_all(bind=engine)
气味当然是我导入和创建所有模型所必须经历的箍。
上面代码的原因是,单元测试时,每次测试调用一次init_db
(在setup()
中,如suggested in the same tutorial),但只会在第一次执行导入, 因此 create_all
将仅在那个时间有效。
不仅如此,现在在应用程序运行期间共享一个会话,我在参数化负单元测试(即预期某种失败的参数化单元测试)中遇到问题:测试的第一个实例将触发失败(例如登录失败,请参阅 test_login_validate_input
in the tutorial)并正确退出,而所有后续将提前退出,因为 db_session
应该首先回滚。显然数据库初始化有问题。
初始化数据库的正确方法 (TM) 是什么?
我最终决定重构该应用,使其使用 Flask-SQLAlchemy。
简而言之,应用程序现在执行如下操作:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
db.init_app(app)
# ...
事后看来,这绝对是一种更简洁的方法。 一开始让我失望的是 tutorial 中的这个条目(粗体是我的):
Because SQLAlchemy is a common database abstraction layer and object relational mapper that requires a little bit of configuration effort, there is a Flask extension that handles that for you. This is recommended if you want to get started quickly.
我以某种方式将其解读为“使用 Flask-SQLAlchemy 扩展程序将允许您偷工减料,您可能最终会为此付费”。
这是非常早期的阶段,但到目前为止,在使用上述扩展的灵活性方面无需付出任何代价。