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