How to check whether SQLAlchemy session is dirty or not
Solution 1
you're looking for a net count of actual flushes that have proceeded for the whole span of the session's transaction; while there are some clues to whether or not this has happened (called the "snapshot"), this structure is just to help with rollbacks and isn't strong referencing. The most direct route to this would be to track "after_flush" events, since this event only emits if flush were called and also that the flush found state to flush:
from sqlalchemy import event
import weakref
transactions_with_flushes = weakref.WeakSet()
@event.listens_for(Session, "after_flush")
def log_transaction(session, flush_context):
for trans in session.transaction._iterate_parents():
transactions_with_flushes.add(trans)
def session_has_pending_commit(session):
return session.transaction in transactions_with_flushes
edit: here's an updated version that's a lot simpler:
from sqlalchemy import event
@event.listens_for(Session, "after_flush")
def log_transaction(session, flush_context):
session.info['has_flushed'] = True
def session_has_pending_commit(session):
return session.info.get('has_flushed', False)
Solution 2
Here is my solution based on @zzzeek's answer and updated comment. I've unit tested it and it seems to play well with rollbacks (a session is clean after issuing a rollback):
from sqlalchemy import event
from sqlalchemy.orm import Session
@event.listens_for(Session, "after_flush")
def log_flush(session, flush_context):
session.info['flushed'] = True
@event.listens_for(Session, "after_commit")
@event.listens_for(Session, "after_rollback")
def reset_flushed(session):
if 'flushed' in session.info:
del session.info['flushed']
def has_uncommitted_changes(session):
return any(session.new) or any(session.deleted) \
or any([x for x in session.dirty if session.is_modified(x)]) \
or session.info.get('flushed', False)
Solution 3
The session has a dirty attribute
session.dirty
persistent objects which currently have changes detected (this collection is now created on the fly each time the property is called)
sqlalchemy.orm.session.Session.dirty
Related videos on Youtube
Giovanni Mascellani
Postdoc in Mathematics at Université Libre de Bruxelles, Belgium. Developer for the Debian Project. And a few other things...
Updated on September 15, 2022Comments
-
Giovanni Mascellani over 1 year
I have a SQLAlchemy
Session
object and would like to know whether it is dirty or not. The exact question what I would like to (metaphorically) ask theSession
is: "If at this point I issue acommit()
or arollback()
, the effect on the database is the same or not?".The rationale is this: I want to ask the user wether he wants or not to confirm the changes. But if there are no changes, I would like not to ask anything. Of course I may monitor myself all the operations that I perform on the
Session
and decide whether there were modifications or not, but because of the structure of my program this would require some quite involved changes. If SQLAlchemy already offered this opportunity, I'd be glad to take advantage of it.Thanks everybody.
-
Giovanni Mascellani about 11 yearsThis doesn't solve my problem: there may be no dirty objects, but if some change was already flushed to the database, I would still want to know about it.
-
Giovanni Mascellani about 11 yearsApart from the fact of being private, it apparently has the same problem as Yoriz's answer: I want to detect also changes there were already flushed, but not committed.
-
aman.gupta about 11 yearsHmm, ok. I don't have any other suggestion except to post on the SQLAlchemy group (groups.google.com/forum/?fromgroups#!forum/sqlalchemy) and if you get an answer, post it here.
-
Giovanni Mascellani over 9 yearsI notice this answer after an year and a half... Thanks, it is just what I needed. I enhanced it by adding another check: transactions are actually added to
transactions_with_flushes
only if there is something insession.new
,session.deleted
orsession.dirty
. Moreover objects insession.dirty
are first tested withsession.is_modified
. The final implementation is in this project of mine: github.com/giomasce/liturgy/blob/master/database.py#L19. It appears to work, although I did not perform maniacal testing. -
Giovanni Mascellani over 7 years
_iterate_parents
has been removed from SQLAlchemy. Now parent iteration must be done by hand. See the same link above to see my implementation, which appear to work. -
zzzeek over 7 yearsThis answer is strange anyway. Session has an .Info dictionary these days. Put "flushed=True" in it for after_flush and remove it in after_commit. Im not sure why I have it putting all the SessionTransaction objects in a dictionary.
-
rdrey almost 6 years@zzzeek does this play well with rollbacks or would we need to hook into
after_
events for those, too? Could you update your answer with the info dict approach, please? -
zzzeek almost 6 years@rdrey the commit/rollback is independent of the flush(). the above recipe will also tell you if data has been flushed that would be impacted by a rollback() also.