Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attaching a pre-built query to a scoped_session in SQLAlchemy

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!

like image 327
snoopy91 Avatar asked Apr 28 '17 17:04

snoopy91


People also ask

What is Scoped_session in SQLAlchemy?

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.

What does Session rollback () do?

Rolling Back. Session. rollback() rolls back the current transaction, if any.

What is _sa_instance_state in SQLAlchemy?

_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).

What does Session flush do in SQLAlchemy?

session. flush() communicates a series of operations to the database (insert, update, delete). The database maintains them as pending operations in a transaction.


1 Answers

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!

like image 179
snoopy91 Avatar answered Oct 14 '22 02:10

snoopy91