在测试期间,烧瓶课程间歇性地导致异常 sqlalchemy.exc.invalidrequesterror:table's sessions'已定义为此元数据实例。在现有表对象上重新定义选项和列指定“ Extend_existing = true”。
我正在使用 pytest ,由于异常,我一直在经历间歇性测试失败 sqlalchemy.exc.invalidrequesterror:table'sessions'已定义为此元数据实例。在现有表对象上重新定义选项和列。仅当某些测试一起运行,一次单独运行一个测试时才发生,它们通过。直到几天前,我一直忽略了这个问题。目前,我有大约396(三百和九十六个)测试用例,只是偶然地,我一起进行了大约三(3)个测试,两(2)个始终失败,但有一个例外,只有一(1)个通过。<<<<<<<<<<<<<<<<<<< /p>
事实证明,这是Flask-Session 0.4.0的持续问题,并且仍然存在于最新版本 0.4.0 中。最新截至2022年11月24日。 我的研究导致了以下帖子,以及报告相同问题的其他文章: How do you resolve 'Already defined in this MetaData Instance' Error with Flask Pytest, SqlAlchemy, and Flask Sessions? user Tassaron在01/01/2021上的答案导致以下URL: 只有在未通过条件上尚未存在的情况下,一般共识似乎仅为数据库会话表创建模型: 已经提出了几年前,但由于某些原因,作者尚未实施。 tassaron,她本人在https://github.com/tassaron/muffin-shop/blob/main/src/helpers/main/session_interface.py中实施了这一点: 与原始的Flask-Session 0.4.0相比: in tassaronSessionInterface ,首先创建了 session model 时,它也将分配给 db new Attribute session_ext_session_model 之后使用 db.session_ext_session_model 。 除了在测试期间间歇性地提出的例外外,Flask-Session 0.4.0的工作正常。我想尽可能坚持下去。以下是我的尝试,感觉就像是解决方法,一个黑客而不是解决方案,我暂时对此表示满意: 要使用此实现,请导入 fixedSession at session ,然后按照正常进行: flask-session 是固定的,我只需删除 fixed_session.py 而无需更新任何代码 - 但是,我应该更新导入,例外很昂贵。 回到我的尝试中,在 pixedsqlalchemysessessionserface 中,我复制了 tassaronsessionsessioninterface 的 db.session_ext_session_model 的想法。以下行是从原始代码中复制的: 这意味着,如果烧瓶式 - 简历在不解决此问题的情况下进行更新,我可能必须更新我的代码! in 类固定,在“覆盖方法”中的以下行 def _get_interface(self,app): 也从原始代码复制了,我从来没有 session_permanent 和 session_key_prefix 在我的环境变量文件中。 使用或没有 sessionsâ表,我的测试和应用程序没有任何问题。如果我删除sessionsâ表,它将按预期创建。 调查这个问题一直很有趣。我对我的代码不太满意,但暂时对我有用。希望作者将来会在将来进行修复。同时,我真的希望这篇文章可以帮助遇到同样问题的其他人。感谢您的阅读和保持安全。
if table not in self.db.metadata:
class TassaronSessionInterface(SessionInterface):
...
def __init__(self, app, db):
...
if table not in self.db.metadata:
# ^ Only create Session Model if it doesn't already exist
# Fixes the SQLAlchemy "extend_existing must be true" exception during tests
class Session(self.db.Model):
...
self.sql_session_model = db.session_ext_session_model = Session
else:
self.sql_session_model = db.session_ext_session_model
class SqlAlchemySessionInterface(SessionInterface):
...
def __init__(self, app, db, table, key_prefix, use_signer=False,
permanent=True):
...
class Session(self.db.Model):
...
self.sql_session_model = Session
Content of fixed_session.py
from flask_session.sessions import SqlAlchemySessionInterface
from flask_session import Session
class FixedSqlAlchemySessionInterface( SqlAlchemySessionInterface ):
def __init__(self, app, db, table, key_prefix, use_signer=False,
permanent=True):
"""
Assumption: the way I use it, db is always a valid instance
at this point.
"""
if table not in db.metadata:
super().__init__( app, db, table, key_prefix, use_signer, permanent )
db.session_ext_session_model = self.sql_session_model
else:
# print( "`sessions` table already exists..." )
self.db = db
self.key_prefix = key_prefix
self.use_signer = use_signer
self.permanent = permanent
self.has_same_site_capability = hasattr(self, "get_cookie_samesite")
self.sql_session_model = db.session_ext_session_model
class FixedSession( Session ):
def _get_interface(self, app):
config = app.config.copy()
if config[ 'SESSION_TYPE' ] != 'sqlalchemy':
return super()._get_interface( app )
else:
config.setdefault( 'SESSION_PERMANENT', True )
config.setdefault( 'SESSION_KEY_PREFIX', 'session:' )
return FixedSqlAlchemySessionInterface(
app, config['SESSION_SQLALCHEMY'],
config['SESSION_SQLALCHEMY_TABLE'],
config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
config['SESSION_PERMANENT'] )
try:
from xxx.yyyy.fixed_session import FixedSession as Session
except ImportError:
from flask_session import Session
self.db = db
self.key_prefix = key_prefix
self.use_signer = use_signer
self.permanent = permanent
self.has_same_site_capability = hasattr(self, "get_cookie_samesite")
config.setdefault( 'SESSION_PERMANENT', True )
config.setdefault( 'SESSION_KEY_PREFIX', 'session:' )