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 <tontho...@gmail.com> 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 sqlalchemy+unsubscr...@googlegroups.com.
>> To post to this group, send email to sqlalchemy@googlegroups.com.
>> 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 sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to