Hello,
Some legacy code at work that I don't fully understand (and whose authors
are all long gone) does the following to eventually insert some values into
the table it gets:
meta = sqlalchemy.MetaData(bind=session.get_bind())
meta.reflect()
table = sqlalchemy.Table(TableName.__tablename__, meta, autoload=True)
The meta.reflect() call fails if the session comes from a sessionmaker
bound to a copy of an engine that has a non-default isolation level, for
example:
engine = sqlalchemy.create_engine(setup)
maker = sqlalchemy.orm.sessionmaker(bind=engine.execution_options(
isolation_level='SERIALIZABLE'))
session = maker()
The error message it gives is "AttributeError: 'OptionEngine' object has no
attribute 'engine'". The reason seems to be that in this case,
session.get_bind() returns an OptionEngine, which seems to be a facade
around the original Engine; it proxies to the original one.
See below for a minimal complete example [A] with a Conda environment [B].
I can get around this by checking whether the get_bind() call gives me an
OptionEngine, and pull the proxied Engine out of the object if it does, but
that involves using internal SQLAlchemy object variables and might break in
the future.
1. Should what I'm doing here work at all? That is, should OptionEngine
define 'engine' and have it resolve to whatever original Engine it was
created from?
2. If not, and what I've inherited is horrifying, does anyone have ideas on
how to make it better? The only comments around this piece of code say they
do this dance to be able to insert values into columns that are in the DB
table but not the SQLAlchemy ORM table.
Thank you,
Gunnar
[A] Minimal example (done here in sqlite3, also works on SQL Server):
import sqlalchemy
import sqlalchemy.orm
import sqlalchemy.ext.declarative
Base = sqlalchemy.ext.declarative.declarative_base()
class Foo(Base):
__tablename__ = 'foos'
id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
val = sqlalchemy.Column(sqlalchemy.Integer)
if __name__ == '__main__':
db = sqlalchemy.create_engine('sqlite:///')
Foo.metadata.create_all(db)
maker = sqlalchemy.orm.sessionmaker(bind=db.execution_options(
isolation_level='SERIALIZABLE'))
session = maker()
bind = session.get_bind()
meta = sqlalchemy.MetaData(bind=bind)
meta.reflect()
table = sqlalchemy.Table(Foo.__tablename__, meta, autoload=True)
vals = list(range(100))
ins = table.insert().values(vals)
session.execute(ins)
session.commit()
session.close()
[B] Conda environment that reproduces the problem in the example:
# Name Version Build Channel
_libgcc_mutex 0.1 main
ca-certificates 2019.5.15 0
certifi 2019.6.16 py37_0
libedit 3.1.20181209 hc058e9b_0
libffi 3.2.1 hd88cf55_4
libgcc-ng 9.1.0 hdf63c60_0
libstdcxx-ng 9.1.0 hdf63c60_0
ncurses 6.1 he6710b0_1
openssl 1.1.1c h7b6447c_1
pip 19.1.1 py37_0
python 3.7.3 h0371630_0
readline 7.0 h7b6447c_5
setuptools 41.0.1 py37_0
sqlalchemy 1.3.5 py37h7b6447c_0
sqlite 3.28.0 h7b6447c_0
tk 8.6.8 hbc83047_0
wheel 0.33.4 py37_0
xz 5.2.4 h14c3975_4
zlib 1.2.11 h7b6447c_3
--
SQLAlchemy -
The Python SQL Toolkit and Object Relational Mapper
http://www.sqlalchemy.org/
To post example code, please provide an MCVE: Minimal, Complete, and Verifiable
Example. See http://stackoverflow.com/help/mcve for a full description.
---
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sqlalchemy.
To view this discussion on the web visit
https://groups.google.com/d/msgid/sqlalchemy/d81b41c0-5e38-4601-a487-c763d3896b91%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.