- edited description
Using "configure" on scoped_session does not bind engine properly
Using SQLAlchemy 1.1.9 (though it happened with 1.1.8 too) with Python 2.7.13 on OS X
Using scoped_session.configure
to change the engine doesn't bind the engine properly. For instance, if you do something like this:
session = orm.scoped_session(orm.sessionmaker())
engine = create_engine('sqlite://')
session.configure(bind=engine)
session.close_all()
engine = create_engine('postgresql://someuser:somepass@localhost:5432/db',
json_serializer=serialize, json_deserializer=deserialize)
session.configure(bind=engine)
print session.bind # will output `Engine(sqlite://)`
It results in errors such as the one you see below (if you start with sqlite, but then switch to postgres and try to query tables).
If you're wondering WHY we would want to do this, it is for testing. Our test suite tries to make sure that everything works ok with both sqlite and postgresql. We can't just create a new instance of the scoped_session, as it will break the factories and various other places that call for a session at load time (we could probably find some way to make it work with two different sessions, but I don't want to do that unless I have to).
Traceback (most recent call last):
File "/vagrant/apps/skedup/tests/base.py", line 28, in setUp
db.session.commit()
File "/usr/local/lib/python2.7/site-packages/sqlalchemy/orm/scoping.py", line 157, in do
return getattr(self.registry(), name)(*args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 874, in commit
self.transaction.commit()
File "/usr/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 459, in commit
self._assert_active(prepared_ok=True)
File "/usr/local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 276, in _assert_active
% self._rollback_exception
InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (exceptions.AttributeError) 'SQLiteDialect_pysqlite' object has no attribute '_json_serializer'
Doing something like this totally fixes it (manually assigning the engine to session.bind
), but it seems like it shouldn't be required:
session = orm.scoped_session(orm.sessionmaker())
engine = create_engine('sqlite://')
session.configure(bind=engine)
session.close_all()
engine = create_engine('postgresql://someuser:somepass@localhost:5432/db',
json_serializer=serialize, json_deserializer=deserialize)
session.configure(bind=engine)
session.bind = engine # <---- this is the key to making it work
print session.bind # will output `Engine(postgresql://someuser:somepass@localhost:5432/db)`
Comments (5)
-
reporter -
reporter - edited description
-
repo owner Your test as written does produce the correct output, because you are not actually instantiating the session:
from sqlalchemy import orm, create_engine session = orm.scoped_session(orm.sessionmaker()) engine = create_engine('sqlite://') session.configure(bind=engine) session.close_all() engine = create_engine('postgresql://someuser:somepass@localhost:5432/db') session.configure(bind=engine) print session.bind
output:
Engine(postgresql://someuser:***@localhost:5432/db)
the issue you're having is that you are instantiating a session first, then changing the "bind" on the
sessionmaker
that produces a new session:from sqlalchemy import orm, create_engine session = orm.scoped_session(orm.sessionmaker()) engine = create_engine('sqlite://') session.configure(bind=engine) session.connection() session.close_all() engine = create_engine('postgresql://someuser:somepass@localhost:5432/db') session.configure(bind=engine) print session.bind
in the output, there is in fact a handy warning as well telling you as much:
#! python test.py /home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/scoping.py:106: SAWarning: At least one scoped session is already present. configure() can not affect sessions that have already been created. warn('At least one scoped session is already present. ' Engine(sqlite://)
the solution is to remove the session that's instantiated:
from sqlalchemy import orm, create_engine session = orm.scoped_session(orm.sessionmaker()) engine = create_engine('sqlite://') session.configure(bind=engine) session.connection() session.remove() engine = create_engine('postgresql://someuser:somepass@localhost:5432/db') session.configure(bind=engine) print session.bind
assuming things are the same on your end, there's no bug here, please confirm thanks!
-
reporter Confirmed. Thank you!
-
reporter - changed status to resolved
Not a bug
- Log in to comment