On Aug 14, 2010, at 12:53 PM, Michael Hipp wrote: > I'm obviously missing some key concept as regards the management of sessions. > This seemingly simple usage fails: > > > def get_new(): > sess = Session() > new = Something() # new orm object > sess.add(new) > sess.commit() > sess.close() > return new > > new = get_new() # request a new Something > print new > print new.id > > Those last 2 print lines throw: > > DetachedInstanceError: Instance <Something at 0x2873ed0> is not bound to > a Session; attribute refresh operation cannot proceed > > I seem to keep butting heads with the session needing to be a global eternal > thing (opposite what the docs recommend).
heh....no, the session is completely ad hoc. What you're missing is that the objects associated with the session should also in most situations be treated as ad-hoc - they represent the state of data within a particular transaction. If you use them outside of a transaction, and not associated with a session that would otherwise have the ability to associate them with a transaction, they are considered to be "detached". "detached" is described at: http://www.sqlalchemy.org/docs/session.html#quickie-intro-to-object-states Where you'll note that "expired" attributes cannot be loaded back from the database. Why are they expired ? Let's look at commit(): http://www.sqlalchemy.org/docs/session.html#committing Second paragraph. "Another behavior of commit() is that by default it expires the state of all instances present after the commit is complete". Why does it do this ? Well, when we have a detached object, and there's no transaction going on (i.e. no connection that can query the DB), we know nothing about what is in the database at that point, so all state on the object is expired. After all, if it had id =12, but some other transaction has deleted row 12, that object is invalid. Without a transaction associated, it would be wrong for us to tell you otherwise. Because we don't know. Now lets assume you don't like this behavior, and your application is just set of operations at a time and nobody else is updating your row (assumptions SQLAlchemy has chosen not to make). Fine. Turn off expire_on_commit. Then when you detach your objects, all their attributes are still present, and you can access them freely. So what if we made this the default. What kinds of complaints, which btw we never get anymore, would we have then ? Well, we'd have (and we had, all the time) this complaint: sess1 = Session() x1 = sess1.query(X).first() x1.foo = 'bar' sess1.commit() sess2 = Session() x2 = sess2.query(X).first() x2.foo = 'bat' sess2.commit() # x1 is still present in the Session's identity map x1 = sess1.query(X).first() assert x1.foo == 'bat' # ugh SQLALCHEMY IS BROKEN ! so we default to the more "transaction hugging" behavior by default - where the error message you get is at least very straightforward, instead of a subtle effect like this one. > > Can someone explain how this is supposed to be done? When you work with mapped objects, you're working with your database. A Session() should be in place and a transaction is in progress. Its only if you want to store mapped objects in some kind of offline cache, or pass them to other usage contexts, that you'd want to keep "detached" objects around. And when you go to use a "detached" object, you put it back into a context where it again is a proxy to some ongoing database operation, i.e. put it in the session for the current operation - often this transfer of state is done via merge(), so that if the destination session already has the object in question present, it will reconcile the incoming state with what it already has. The "load=False" setting of merge() prevents the usage of a SELECT from loading existing state, if you are working with long term immutable data and don't want the extra SELECT emitted. Alternatively, if you really want to pass around detached objects and make use of their detached state, even though that state may be stale or even deleted vs. what's in the database, you can disable expire_on_commit - if you are making ad-hoc sessions for usage in single functions, just pass it to that specific session constructor. -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To post to this group, send email to sqlalch...@googlegroups.com. To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.