Thank, I haven't yet figured out how to make your code more general (without having to specify 'user_id'), but I think it can be done.
In the meantime I came up with a somewhat gross solution of my own. The idea is to declare relation as `viewonly=True` and to delegate sets to an injected write-only relationship (injection is the gross part). I'm curious to hear your opinion on this approach, if possible. Here is the code: > > from sqlalchemy import Column, Integer, String, ForeignKey, Boolean > from sqlalchemy import create_engine, and_ > from sqlalchemy import event > from sqlalchemy.ext.declarative import declarative_base > from sqlalchemy.orm import relationship, mapper > from sqlalchemy.orm import sessionmaker > > > Base = declarative_base() > engine = create_engine('sqlite:///:memory:', echo=False) > Session = sessionmaker(bind=engine) > > > def soft_deleting_relationship(target, primaryjoin): > assert issubclass(target, Base) > rel = relationship(target, > primaryjoin=primaryjoin, > viewonly=True, > uselist=False, > ) > > @event.listens_for(mapper, 'after_configured', once=True) > def configure(): > > key = '_all_child_' + rel.key > parent = rel.parent.class_ > if not hasattr(parent, key): > setattr(parent, key, relationship(target, lazy="noload")) > > @event.listens_for(rel, 'set', active_history=True) > def set(target, value, oldvalue, initiator): > if oldvalue is not None: > oldvalue.delete() > getattr(target, key).append(value) > > return rel > > > class User(Base): > __tablename__ = 'users' > > id = Column(Integer, primary_key=True) > > > class Address(Base): > __tablename__ = 'addresses' > id = Column(Integer, primary_key=True) > email = Column(String, nullable=False) > deleted = Column(Boolean, nullable=False, default=False) > > user_id = Column('user_id', Integer, ForeignKey('users.id')) > > def delete(self): > self.deleted = True > > > User.address = soft_deleting_relationship( > Address, > primaryjoin=lambda: and_(Address.user_id == User.id, > ~Address.deleted), > ) > > > def test(): > Base.metadata.create_all(engine) > sess = Session() > > a1 = Address(email='foo') > u = User(id=1, address=a1) > sess.add_all([u, a1]) > sess.commit() > > a2 = Address(email='bar') > u.address = a2 > sess.commit() > > assert a1.user_id == 1, a1.user_id > assert u.address.id == a2.id > assert a2.id is not None > assert a1.deleted > > > test() > > > -- 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.