python - Modify other objects on update/insert -


i've got 2 mapped objects, parent , child.

class parent(base)     __tablename__ = 'parent'     id = ...     name = ...     date_modified = column(sa_datetime, default=datetime.now,                            onupdate=datetime.now, nullable=false)  class child(base)     __tablename__ = 'child'     id = ...     name = ...     date_modified = column(sa_datetime, default=datetime.now,                            onupdate=datetime.now, nullable=false)     parent = relationship(parent, backref='parent') 

when child updated, want not child.date_modified changed, child.parent.date_modified.

i tried this:

@event.listens_for(child, 'after_update') def modified_listener(mapper, connection, target):     if object_session(target).is_modified(target, include_collections=false):         target.parent.date_modified = datetime.now() 

but doesn't work, because i'm in flush , sawarning: attribute history events accumulated on 1 clean instance within inner-flush event handlers have been reset, , not result in database updates. consider using set_committed_value() within inner-flush event handlers avoid warning.

how can solve sqlalchemy?

basic update-parent-when-child-changes using sqlalchemy events has been covered on site before here , here, in case you're trying update parent during flush, possibly using update default value child, visible after update, or new value entirely. modifying parent in event handler not straightforward might first imagine:

warning

mapper-level flush events allow very limited operations, on attributes local row being operated upon only, allowing sql emitted on given connection. please read fully notes @ mapper-level events guidelines on using these methods; generally, sessionevents.before_flush() method should preferred general on-flush changes.

as you've noticed, simple

target.parent.date_modified = datetime.now() 

in event handler warns:

sawarning: attribute history events accumulated on 1 clean instances within inner-flush event handlers have been reset, , not result in database updates. consider using set_committed_value() within inner-flush event handlers avoid warning.

set_committed_value() allows setting attributes no history events, if set value part of original loaded state.

you've noticed receiving target in after update event handler not guarantee update statement emitted:

this method called instances marked “dirty”, have no net changes column-based attributes, , no update statement has proceeded.

and

to detect if column-based attributes on object have net changes, , therefore resulted in update statement, use object_session(instance).is_modified(instance, include_collections=false).

so solution use information held in event target emit update statement on parent table using given connection, , check if parent object present in session , set committed value of it:

from sqlalchemy import event sqlalchemy.orm.attributes import set_committed_value sqlalchemy.orm.session import object_session  @event.listens_for(child, 'after_update')                 def receive_child_after_update(mapper, connection, target):     session = object_session(target)                             if not session.is_modified(target, include_collections=false):         return                                                          new_date_modified = target.date_modified      # avoid touching target.parent relationship attribute ,     # copy date_modified value child parent.     # warning: overwrite possible other updates parent's     # date_modified.     connection.execute(         parent.__table__.         update().                 values(date_modified=new_date_modified).         where(parent.id == target.parent_id))      parent_key = session.identity_key(parent, target.parent_id)      try:         the_parent = session.identity_map[parent_key]      except keyerror:         pass      else:         # if parent object present in session, update         # date_modified attribute **in python only**, reflect         # updated db state local transaction.         set_committed_value(             the_parent, 'date_modified', new_date_modified) 

Comments

Popular posts from this blog

sql - invalid in the select list because it is not contained in either an aggregate function -

Angularjs unit testing - ng-disabled not working when adding text to textarea -

How to start daemon on android by adb -