I'm receiving a weird error, AttributeError: 'scoped_session' object has no attribute '_autoflush'
, when attempting to execute a pre-built query on a scoped_session in the SQLAlchemy ORM.
I'm using a context manager to yield a scoped_session,
@contextmanager
def connect(my_session_factory):
session = scoped_session(my_session_factory)
try:
yield session
except Exception as exception:
session.rollback()
raise exception
finally:
session.close()
and then am using it like so:
from sqlalchemy.orm import Query
query = Query(my_model).offset(my_offset).limit(my_limit)
with connect(my_session_factory) as session:
instances = query.with_session(session).all()
return instances
This, however, raises the exception above.
I note also that queries of the form session.query(my_model)
work just fine.
Where am I going wrong? Many thanks!
sqlalchemy. A scoped_session is constructed by calling it, passing it a factory which can create new Session objects. A factory is just something that produces a new object when called, and in the case of Session , the most common factory is the sessionmaker , introduced earlier in this section.
Rolling Back. Session. rollback() rolls back the current transaction, if any.
_sa_instance_state is a non-database-persisted value used by SQLAlchemy internally (it refers to the InstanceState for the instance. While not directly relevant to this section, if we want to get at it, we should use the inspect() function to access it).
session. flush() communicates a series of operations to the database (insert, update, delete). The database maintains them as pending operations in a transaction.
Okay - I don't have an answer to the question as posed, but I do seem to have a workaround.
The issue appears to be with the proxying behaviour of the scoped_session
object. As I understand it, the scoped_session()
method takes a sessionmaker
object and uses that to create thread-local session
object. The scoped_session()
method, however, does not return this thread-local session
. Instead it returns a scoped_session
object that in some way (on which I'm not entirely clear) houses the thread-local session
. To access this thread-local session directly, you can do scoped_session.registry()
, or instead simply scoped_session()
where scoped_session
here is a scoped_session
object that has been returned by the scoped_session
method.
my_scoped_session = scoped_session(my_session_factory)
my_local_session = my_scoped_session()
Now here's the issue: the documentation seems to suggest that calls such as my_scoped_session.query(...).all()
and my_local_session.query(...).all()
are equivalent, thanks to the proxying behaviour of the scoped_session
object. I have found this to be true for the most part, however not so in my original problem case.
If you do my_query = Query(...)
(i.e. build a non-session bound query), and then attach that to a scoped_session
object (hoping to take advantage of the scoped_session
proxying machinery, such that my_query
is handled in the context of the scoped_session
thread-local session
), by way of my_instances = my_query.with_session(my_scoped_session).all()
or similar, you get the traceback in my original question.
My workaround is to skip over the scoped_session
object's proxying machinery altogether, and instead to bind the my_query
directly to my_local_session
.
my_query = Query(...).filter(...).sort(...)
my_instances = my_query.with_session(my_local_session).all()
This seems to work out. If, however, anyone would care to chime in on the dangers (if any?) of working with scoped_session(my_session_factory)()
directly, rather than with scoped_session(my_session_factory)
(which most online tutorials seem to do), then I'd be grateful!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With