Thanks a lot for this helpfull and precise answer. I'll tend to say "as usual" :).
Have a nice day Gaston Le 07/02/2018 à 16:13, Mike Bayer a écrit : > On Wed, Feb 7, 2018 at 7:16 AM, tonthon <[email protected]> wrote: >> Hi, >> >> I'd like to setup bidirectionnal data synchornization between the lastname >> attribute of two related models >> >> class User(Base): >> __tablename__ = 'users' >> id = Column(Integer, primary_key=True) >> lastname = Column(String(50)) >> userdatas = relationship('UserDatas', >> primaryjoin='User.id==UserDatas.user_id', back_populates='user', >> uselist=False) >> >> class UserDatas(Base): >> __tablename__ = 'userdatas' >> id = Column(Integer, primary_key=True) >> lastname = Column(String(50)) >> user_id = Column(ForeignKey('users.id')) >> user = relationship('User', primaryjoin='User.id==UserDatas.user_id') >> >> >> I thought I could do something like this : >> >> def sync_lastname_user_to_userdatas(target, value, oldvalue, initiator): >> target.userdatas.lastname = value >> >> listen(User.lastname, 'set', sync_lastname_user_to_userdatas) >> >> def sync_lastname_userdatas_to_user(target, value, oldvalue, initiator): >> target.user.lastname = value >> >> listen(UserDatas.lastname, 'set', sync_lastname_userdatas_to_user) >> >> >> The obvious problem here is the infinite loop that is generated >> >> So : >> Is there a way to set an attribute without firing the 'set' event ? >> >> I tend to think there is a better way to do that, does anybody have an >> advice to share ? > below is an example of the most correct way to do this, to support > this use case I will add the "initiator" argument to the > set_attribute() function within SQLAlchemy but this will work in all > 1.1, 1.2 versions for now: > > from sqlalchemy import Column, Integer, String, ForeignKey > from sqlalchemy.orm import relationship > from sqlalchemy.ext.declarative import declarative_base > from sqlalchemy import event > from sqlalchemy import inspect > > > def set_attribute(instance, key, value, initiator=None): > """Set the value of an attribute, firing history events. > > This function is copied from the attributes module but adds the > "initiator" argument. > > """ > state = inspect(instance) > dict_ = state.dict > state.manager[key].impl.set(state, dict_, value, initiator) > > Base = declarative_base() > > > class User(Base): > __tablename__ = 'users' > id = Column(Integer, primary_key=True) > lastname = Column(String(50)) > userdatas = relationship( > 'UserDatas', primaryjoin='User.id==UserDatas.user_id', > back_populates='user', uselist=False) > > > class UserDatas(Base): > __tablename__ = 'userdatas' > id = Column(Integer, primary_key=True) > lastname = Column(String(50)) > user_id = Column(ForeignKey('users.id')) > user = relationship('User', primaryjoin='User.id==UserDatas.user_id') > > > @event.listens_for(User.lastname, "set") > def sync_lastname_user_to_userdatas(target, value, oldvalue, initiator): > parentclass = initiator.parent_token.parent.class_ > if parentclass is User: > set_attribute(target.userdatas, "lastname", value, initiator) > > > @event.listens_for(UserDatas.lastname, 'set') > def sync_lastname_userdatas_to_user(target, value, oldvalue, initiator): > parentclass = initiator.parent_token.parent.class_ > if parentclass is UserDatas: > set_attribute(target.user, "lastname", value, initiator) > > u1 = User(userdatas=UserDatas()) > > u1.lastname = 'foo' > > assert u1.userdatas.lastname == u1.lastname == 'foo' > > u1.userdatas.lastname = 'bar' > assert u1.userdatas.lastname == u1.lastname == 'bar' > > > > > > > >> Thanks in advance >> >> Regards, >> Gaston Tjebbes >> >> https://www.majerti.fr >> >> -- >> SQLAlchemy - >> The Python SQL Toolkit and Object Relational Mapper >> >> http://www.sqlalchemy.org/ >> >> To post example code, please provide an MCVE: Minimal, Complete, and >> Verifiable Example. See http://stackoverflow.com/help/mcve for a full >> description. >> --- >> 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 https://groups.google.com/group/sqlalchemy. >> For more options, visit https://groups.google.com/d/optout. -- SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper http://www.sqlalchemy.org/ To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description. --- 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 https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.
