I'm building a pyramid application using pyramid_tm and
ZopeTransactionExtension. We've written a little subscriber on NewResponse
that writes out some values to a log file about the current user
(request.user.id) after each request. For anybody that knows pyramid
pretty well, we set the request.user property
using config.set_request_property(get_user, 'user', reify=True), but
basically we look up the User object once in our db and then cache it as
request.user for the lifetime of the request.
For the most part this is working fine, except for in the case that the
User object gets modified during the request (change their avatar, name,
password, whatever). When this happens, we get a Detached Instance
exception when we try to reference the 'id' field of request.user. It's
worth pointing out that pyramid_tm runs and commits our transaction before
our NewResponse subscriber fires, which means that request.user has already
been flushed and committed to the database and appears to be detached (in
fact, we can merge it back into our session and continue using it as
normal, see Solution #3 below).
We've found 3 work-arounds that seem to give us the desired behavior, but
I'm not really sure which one is better.
Solution 1
----------
# setting up the session
DBSession = scoped_session(sessionmaker(bind=engine,
extension=ZopeTransactionExtension(), expire_on_commit=False))
# in the subscriber
print request.user.id # does 0 additional db queries
Solution 2
----------
# setting up the session
DBSession = scoped_session(sessionmaker(bind=engine,
extension=ZopeTransactionExtension(keep_session=True)))
# in the subscriber
print request.user.id # does an additional SELECT query
Solution 3
----------
# setting up the session
DBSession = scoped_session(sessionmaker(bind=engine,
extension=ZopeTransactionExtension()))
# in the subscriber
session = DBSession()
user = session.merge(request.user) # does an additional SELECT query
print user.id
Without using any of these solutions, we sure enough get the Exception:
DetachedInstanceError: Instance <User at 0x26d5990> is not bound to a
Session; attribute refresh operation cannot proceed
In this case, it seems Solution 1 is the best as it doesn't do any
additional SQL queries (and I'm willing to accept that occasionally I might
be writing a stale User object to disk), but is there any other downside to
this approach? After my subscriber runs the web request is finished, so
theoretically it doesn't matter that the objects are not expired, correct?
Is there a better approach here that I am missing?
Thanks!
--
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 http://groups.google.com/group/sqlalchemy?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.