This can be worked around using the cascade_backrefs option which in 1.4 will default to the better setting. you can see what's happening here in the stack trace where you will see some of this:
File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 1017, in set
value = self.fire_replace_event(state, dict_, value, old, initiator)
File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 1039, in fire_replace_event
value = fn(
File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 1472, in emit_backref_from_scalar_set_event
child_impl.append(
File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 1244, in append
collection.append_with_event(value, initiator)
File "/home/classic/dev/sqlalchemy/lib/sqlalchemy/orm/collections.py", line 654, in append_with_event
see the "backref" ? your program seeks to add objects to the session using session.merge() exclusively. but above you can see the offending object is being "added", that's wrong, it's being "added" due to an "append" to your collection, which is from a backref, all of that is what you don't want and it's because of "origin=tokyo, destinationInput=luggage" are reverse-cascading your object directly into the session including the "origin" which you'd prefer is merged.
class Gate(Thing):
__tablename__ = "gates"
id = Column(Integer, ForeignKey("
things.id"), primary_key=True)
origin = relationship(
Location,
foreign_keys=[originId],
backref=backref("originGates", cascade_backrefs=False),
cascade_backrefs=False,
)
originInput = relationship(
LocationInput,
foreign_keys=[originInputId],
backref=backref("originInputGates", cascade_backrefs=False),
cascade_backrefs=False,
)
destination = relationship(
Location,
foreign_keys=[destinationId],
backref=backref("destinationGates", cascade_backrefs=False),
cascade_backrefs=False,
)
destinationInput = relationship(
LocationInput,
foreign_keys=[destinationInputId],
backref=backref("destinationInputGates", cascade_backrefs=False),
cascade_backrefs=False,
)
in 1.4, cascade_backrefs defaults to False so you won't need to do this anymore.