简体   繁体   中英

Marking an object as clean in SQLAlchemy ORM

Is there any way to explicitly mark an object as clean in the SQLAlchemy ORM?

This is related partly to a previous question on bulk update strategies .

I want to, within a before_flush event listener mark a bunch of object as actually not needing to be flushed. This is due to them being manually synced with the database by other means.

I have tried the strategy below, but it results in the object being removed from the session, which then can cause problems later when a lazy load happens.

@event.listens_for(SignallingSession, 'before_flush')
def before_flush(session, flush_context, instances):
    ledgers = []

    if session.dirty:
        for elem in session.dirty:
            if ( session.is_modified(elem, include_collections=False) ):
                if isinstance(elem, Wallet):
                    session.expunge(elem) # causes problems later
                    ledgers.append(Ledger(id=elem.id, amount=elem.balance))

    if ledgers:
        session.bulk_save_objects(ledgers)
        session.execute('UPDATE wallet w JOIN ledger l on w.id = l.id SET w.balance = l.amount')
        session.execute('TRUNCATE ledger')

I want to do something like:

session.dirty.remove(MyObject)

But that doesn't work as session.dirty is a computed property, not a regular attribute. I've been digging around the instrumentation code, but can't see how I might fool the dirty list to not contain something. I see there is also a history on the object state that will need taking care of as well.

Any ideas? The underlying database is MySQL if that makes any difference.

-Matt

When you modify the database outside of the ORM, you can let the ORM know the current database state by using set_committed_value() .

Example:

wallet = session.query(Wallet).filter_by(id=123)
wallet.balance = 0
session.execute("UPDATE wallet SET balance = 0 WHERE id = 123;")
set_committed_value(wallet, "balance", 0)
session.commit()  # won't issue additional SQL to update wallet

If you really wanted to mark the instance as not dirty, you can muck with the internals of SQLAlchemy:

state = inspect(p)
session.identity_map._modified.discard(state)
state.modified = False
print(p in session.dirty)  # False

Let me summarize this insanity.

from sqlalchemy.orm import attributes

attributes.instance_state(your_object).committed_state.clear()

Easy. (no)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM