【发布时间】:2022-01-12 15:42:06
【问题描述】:
Flask tutorial(以及许多other tutorialsout there)表明engine、db_session 和Base(declarative_metadata 的一个实例)都是在导入时创建的。
这会产生一些问题,其中一个是 DB 的 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)?
【问题讨论】:
标签: python postgresql flask sqlalchemy